home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 …ember: Reference Library / Dev.CD Dec 94.toast / Technical Documentation / Mac Tech Notes (DocViewer) / OV • Overview / OV04 Compatibility Why&How / OV04 Compatibility Why&How
Encoding:
Text File  |  1994-10-19  |  164.1 KB  |  295 lines  |  [ONLN/HLX2]

  1. OV 4 - Compatibility: Why & How
  2. Overview    
  3. Revised by:        March 1988
  4. Written by:    Bob Johnson    February 1987
  5. While creating or revising any program for the Macintosh, you should be aware of the most common reasons why programs fail on various versions of the Macintosh. This note will detail some common failure modes, why they occur, and how to avoid them.
  6. We’ve tried to explain the issues in depth, but recognize that not everyone is interested in every issue. For example, if your application is not copy protected, you’re probably not very interested in the section on copy protection. That’s why we’ve included the outline form of the technical note. The first two pages outline the problems and the solutions that are detailed later. Feel free to skip around at will, but remember that we’re sending this enormous technical note because the suggestions it provides may save you hasty compatibility revisions when we announce a new machine.
  7. We know it’s a lot, and we’re here to help you if you need it. Our address (electronic and physical) is on page two—contact us with any questions—that’s what we’re here for!
  8. Compatibility: the outline
  9. Don’t assume the screen is a fixed size
  10. To get the screen size:
  11.     • check the QuickDraw global screenBits.bounds
  12. Don’t assume the screen is in a fixed location
  13. To get the screen location:
  14.     • check the QuickDraw global screenBits.baseAddr
  15. Don’t assume that rowBytes is equal to the width of the screen
  16. To get the number of bytes on a line:
  17.     • check the QuickDraw global screenBits.rowBytes
  18. To get the screen width:
  19.     • check the QuickDraw global screenBits.bounds.right
  20. To do screen-size calculations:
  21.     • Use LongInts
  22. Don’t write to or read from nil Handles or nil Pointers
  23. Don’t create or Use Fake Handles
  24. To avoid creating or using fake handles:
  25.     • Always let the Memory Manager perform operations with handles
  26.     • Never write code that assigns something to a master pointer
  27. Don’t write code that modifies itself
  28. Self modifying code will not live across incarnations of the 68000
  29. Think carefully about code designed strictly as copy protection
  30. To avoid copy protection-related incompatibilities:
  31.     • Avoid copy protection altogether
  32.     • Rely on schemes that don’t require specific hardware
  33.     • Make sure your scheme doesn’t perform illegal operations 
  34. Don’t ignore errors
  35. To get valuable information:
  36.     • Check all pertinent calls for errors 
  37.     • Always write defensive code 
  38. Don’t access hardware directly
  39. To avoid hardware-related incompatibilities:
  40.     • Don’t read or write the hardware
  41.     • If you can’t get the support from the ROM, ask the system where the hardware is
  42.     • Use low-memory globals
  43. Don’t use bits that are reserved
  44. To avoid compatibility problems when bit status changes:
  45.     • Don’t use undocumented stuff
  46.     • When using low-memory globals, check only what you want to know
  47. Summary
  48. Minor bugs are getting harder and harder to get away with:
  49.     • Good luck
  50.     • We’ll help
  51.     • AppleLink: MacDTS, MCI: MacDTS
  52.     • U.S. Mail: 20525 Mariani Ave.; M/S 27-T; Cupertino, CA 95014
  53. What it Is
  54. The basic idea is to make sure that your programs will run, regardless of which Macintosh they are being run on. The current systems to be concerned with include:
  55.     • Macintosh 128K    • Macintosh 512Ke
  56.     • Macintosh 512K    • Macintosh Plus
  57.     • Macintosh XL    • Macintosh SE
  58.     • Macintosh II
  59. If you perform operations in a generic fashion, there is rarely any reason to know what machine is running. This means that you should avoid writing code to determine which version of the machine you are running on, unless it is absolutely necessary.
  60. For the purposes of this discussion, the term “programs” will be used to describe any code that runs on a Macintosh. This includes applications, INITs, FKEYs, Desk Accessories and Drivers.
  61. What the “Rules” mean
  62. Compatibility across all Macintosh computers (which may sound like it involves more work for you) may actually mean that you have less work to do, since it may not be necessary to revise your program each time Apple brings out a new computer or System file. Users, as a group, do not understand compatibility problems; all they see is that the program does not run on their system.
  63. The benefits of being compatible are many-fold: your customers/users stay happy, you have less programming to do, you can devote your time to more valuable goals, there are fewer versions to deal with, your code will probably be more efficient, your users will not curse you under their breath, and your outlook on life will be much merrier.
  64. Now that we know what being compatible is all about, recognize that nobody is requiring you to be compatible with anything. Apple does not employ roving gangs of thought police to be sure that developers are following the recommended guidelines. Furthermore, when the guidelines comprise 1200 pages of turgid prose (Inside Macintosh), you can be expected to miss one or two of the “rules.” It is no sin to be incompatible, nor is it a punishable offense. If it were, there would be no Macintosh programs, since virtually all developers would be incarcerated. What it does mean, however, is that your program will be unfavorably viewed until it steps in line with the current system (which is a moving target). If a program becomes incompatible with a new Macintosh, it usually requires rethinking the offending code, and releasing a new version. You may read something like “If the developers followed Apple guidelines, they would be compatible with the transverse-hinged diatomic quark realignment system.” This means that if you made any mistakes (you read all 1200 pages carefully, right?), you will not be compatible. It is extremely difficult to remain completely compatible, particularly in a system as complex as the Macintosh. The rules haven’t changed, but what you can get away with has. There are, however, a number of things that you can do to improve your odds—some of which will be explained here.
  65. It’s your choice
  66. It is still your choice whether you will be concerned with compatibility or not. Apple will not put out a warrant for your arrest. However, if you are doing things that are specifically illegal, Apple will also not worry about “breaking” your program.
  67. Bad Things
  68. The following list is not intended to be comprehensive, but these are the primary reasons why programs break from one version of the system to the next. These are the current top ten commandments:
  69. I    Thou shalt not assume the screen is a fixed size.
  70. II    Thou shalt not assume the screen is at a fixed location.
  71. III    Thou shalt not assume that rowBytes is equal to the width of the screen.
  72. IV    Thou shalt not use nil handles or nil pointers.
  73. V    Thou shalt not create or use fake handles.
  74. VI    Thou shalt not write code that modifies itself.
  75. VII    Thou shalt think twice about code designed strictly as copy protection.
  76. VIII    Thou shalt check errors returned as function results.
  77. IX    Thou shalt not access hardware directly.
  78. X    Thou shalt not use any of the bits that are reserved (unused means reserved).
  79. This has been determined from extensive testing of our diverse software base.
  80. Assuming the screen is a fixed size
  81. Do not assume that the Macintosh screen is 512 x 342 pixels. Programs that do generally have problems on (or special case for) the Macintosh XL, which has a wider screen. Most applications have to create the bounding rectangle where a window can be dragged. This is the boundsRect that is passed to the call:
  82.     DragWindow (myWindowPtr, theEvent.where, boundsRect);
  83. Some ill-advised programs create the boundsRect by something like:
  84.     SetRect (boundsRect, 0,0,342,512);  { oops, this is hard-coded…}
  85. Why it’s Bad
  86. This is bad because it is never necessary to specifically put in the bounding rectangle for the screen. On a Macintosh XL for example, the screen size is 760x364 (and sometimes 608x431 with alternate hardware). If a program uses the hard-coded 0,0,342,512 as a bounding rectangle, end users will not be able to move their windows past the fictitious boundary of 512. If something similar were done to the GrowWindow call, it would make it impossible for users to grow their window to fill the entire screen. (Always a saddening waste of valuable screen real-estate.)
  87. Assuming screen size makes it more difficult to use the program on Macintoshes with big screens, by making it difficult to grow or move windows, or by drawing in strange places where they should not be drawing (outside of windows). Consider the case of running on a Macintosh equipped with one of the full page displays, or Ultra-Large screens. No one who paid for a big screen wants to be restricted to using only the upper-left corner of it.
  88. How to avoid becoming a screening fascist
  89. Never hard code the numbers 512 and 342 for screen dimensions. You should avoid using constants for system values that can change. Parameters like these are nearly always available in a dynamic fashion. Programs should read the appropriate variables while the program is running (at run-time, not at compile time).
  90. Here’s how smart programs get the screen dimensions:
  91.     InitGraf(@thePort); { QuickDraw global variables have to be initialized.}
  92.     …
  93.     boundsRect := screenBits.bounds;   { The Real way to get screen size }
  94.                                                           { Use QuickDraw global variable. }
  95. This is smart, because the program never has to know specifically what the numbers are. All references to rectangles that need to be related to the screen (like the drag and grow areas of windows) should use screenBits.bounds to avoid worrying about the screen size.
  96. Note that this does not do anything remotely like assume that “if the computer is not a standard Macintosh, then it must be an XL.” Special casing for the various versions of the Macintosh has always been suspicious at best; it is now grounds for breaking. (At least with respect to screen dimensions.)
  97. By the way, remember to take into account the menu bar height when using this rectangle. On 128K ROMs (and later) you can use the low-memory global mBarHeight (a word at $BAA). But since we didn’t provide a low-memory global for the menu bar height in the 64K ROMs, you’ll have to hard code it to 20 ($14). (You’re not the only ones to forget the future holds changes.)
  98. How to find fascist screenism in current programs
  99. The easiest way is to exercise your program on one of the Ultra-Large screen Macintoshes. There should be no restrictions on sizing or moving the windows, and all drawing should have no problems. If there are any anomalies in the program’s usage, there is probably a lurking problem. Also, do a global find in the source code to see if the numbers 512 or 342 occur in the program. If so, and if they are in reference to the screen, excise them.
  100. Assuming the screen is at a fixed location
  101. Some programs use a fixed screen address, assuming that the screen location will be the same on various incarnations of the Macintosh. This is not the case. For example, the screen is located at memory location $1A700 on a 128K Macintosh, at $7A700 on a 512K Macintosh, at $F8000 on the Macintosh XL, and at $FA700 on the Macintosh Plus.
  102. Why it’s Bad
  103. When a program relies upon the screen being in a fixed location, Murphy’s Law dictates that an unknowing user will run it upon a computer with the screen in a different location. This usually causes the system to crash, since the offending program will write to memory that was used for something important. Programs that crash have been proven to be less useful than those that don’t.
  104. How to avoid being a base screener
  105. Suffice it to say that there is no way that the address of the screen will remain static, but there are rare occasions where it is necessary to go directly to the screen memory. On these occasions, there are bad ways and not-as-bad ways to do it. A bad way:
  106.     myScreenBase := Pointer ($7A700);  { not good.  Hard-coded number. }
  107. A not-as-bad way:
  108.     InitGraf(@thePort);   { do this only once in a program. }
  109.     …
  110.     myScreenBase := screenBits.baseAddr;  { Good.  Always works. }
  111.                                       {Yet another QuickDraw global variable}
  112. Using the latter approach is guaranteed to work, since QuickDraw has to know where to draw, and the operating system tells QuickDraw where the screen can be found. When in doubt, ask QuickDraw. This will work on Macintosh computers from now until forever, so if you use this approach you won’t have to revise your program just because the screen moved in memory.
  113. If you have a program (such as an INIT) that cannot rely upon QuickDraw being initialized (via InitGraf), then it is possible to use the ScrnBase low-memory global variable (a long word at $824). This method runs a distant second to asking QuickDraw, but is sometimes necessary.
  114. How to find base screeners
  115. The easiest way to find base screeners is to run the offending program on machines that have different screen addresses. If any addresses are being used in a base manner, the system will usually crash. The offending program may also occasionally refuse to draw. Some programs afflicted with this problem may also hang the computer (sometimes known as accessing funny space). Also, do a global find on the source code to look for numbers like $7A700 or $1A700. When found, exercise caution while altering the offending lines.
  116. Assuming that rowbytes is equal to the width of the screen
  117. According to the definition of a bitMap found in Inside Macintosh (p I-144), you can see that rowBytes is the number of actual bytes in memory that are used to determine the bitMap. We know the screen is just a big hunk of memory, and we know that QuickDraw uses that memory as a bitMap. rowBytes accomplishes the translation of a big hunk of memory into a bitMap. To do this, rowBytes tells the system how long a given row is in memory and, more importantly, where in memory the next row starts. For conventional Macintoshes, rowBytes (bytes per Row) * 8 (Pixels per Byte) gives the final horizontal width of the screen as Pixels per Row. This does not have to be the case. It is possible to have a Macintosh screen where the rowBytes extends beyond what is actually visible on the screen. You can think of it as having the screen looking in on a larger bitMap. Diagrammatically, it might look like:
  118. With an Ultra-Large screen, the number of bytes used for screen memory may be in the 500,000 byte range. Whenever calculations are being made to find various locations in the screen, the variables used should be able to handle larger screen sizes. For example, a 16 bit Integer will not be able to hold the 500,000 number, so a LongInt would be required.  Do not assume that the screen size is 21,888 bytes long.  bitMaps can be larger than 32K or 64K.
  119. Why it’s Bad
  120. Programs that assume that all of the bytes in a row are visible may make bad calculations, causing drawing routines to produce unusual, and unreadable, results.  Also, programs that use the rowBytes to figure out the width of the screen rectangle will find that their calculated rectangle is not the real screenBits.Bounds.  Drawing into areas that are not visible will not necessarily crash the computer, but it will probably give erroneous results, and displays that don’t match the normal output of the program.
  121. Programs that assume that the number of bytes in the screen memory will be less than 32768 may have problems drawing into Ultra-Large screens, since those screens will often have more memory than a normal Macintosh screen.  These particular problems do not evidence themselves by crashing the system.  They generally appear as loss of functionality (not being able to move a window to the bottom of the screen), or as drawing routines that no longer look correct.  These problems can prevent an otherwise wonderful program from being used.
  122. How to avoid being a row byter
  123. In any calculations, the rowBytes variable should be thought of as the way to get to the next row on the screen.  This is distinct from thinking of it as the width of the screen.  The width should always be found from screenBits.bounds.right– screenBits.bounds.left.   
  124. It is also inappropriate to use the rectangle to decide how many bytes there are on a row.  Programs that do something like:
  125.     bytesLine := screenBits.bounds.right DIV 8;  { bad use of bounds }
  126.     rightSide := screenBits.rowBytes * 8;        { bad use of rowBytes }
  127. will find that the screen may have more rowBytes than previously thought.  The best way to avoid being a row byter is to use the proper variables for the proper things.  Without the proper mathematical basis to the screen, life becomes much more difficult.  Always do things like:
  128.     bytesLine := screenBits.rowBytes;      { always the correct number }
  129.     rightSide := screenBits.bounds.right;  { always the correct screen size }
  130. It is sometimes necessary to do calculations involving the screen.  If so, be sure to use LongInts for all the math, and be sure to use the right variables (i.e. use LongInts).  For example, if we need to find the address of the 500th row in the screen (500 lines from the top):
  131. VAR    myAddress:    LongInt;
  132.     myRow:    LongInt;        { so the calculations don’t round off. }
  133.     myOffset:    LongInt;    { could easily be over 32768 ... }
  134.     bytesLine:    LongInt;
  135.     myAddress := ord4(screenBits.baseAddr);{start w/the real base address }
  136.     myRow := 500;                  {the row we want to address }
  137.     bytesLine := screenBits.rowBytes;      {the real bytes per line }
  138.     myOffset := myRow * bytesLine;           {lines * bytes per lines gives bytes }
  139.     myAddress := myAddress + myOffset;       {final address of the 500th line }
  140. This is not something you want to do if you can possibly avoid it, but if you simply must go directly to the screen, be careful.  The big-screen machines (Ultra-Large screens) will thank you for it.  If QuickDraw cannot be initialized, there is also the low-memory global screenRow (a word at $106) that will give you the current rowBytes.  
  141. How to find row byters
  142. To find current problems with row byter programs, run them on a machine equipped with Ultra-Large screens and see if any anomalies crop up.  Look for drawing sequences that don’t work right, and for drawing that clips to an imaginary edge.  For source-level inspection, look for uses of the rowBytes variables and be sure that they are being used in a mathematically sound fashion.  Be highly suspicious of any code that uses rowBytes for the screen width.  Any calculations involving those system variables should be closely inspected for round-off errors and improper use.  Search for the number 8.  If it is being used in a calculation where it is the number of bits per byte, then watch that code closely for improper conceptualization.  This is code that could leap out and grab you by the throat at anytime.  Be careful!
  143.     
  144. Using nil Handles or nil Pointers
  145. A nil pointer is a pointer that has a value of 0.  Recognize that pointers are merely addresses in memory.  This means that a nil pointer is pointing to memory location 0.  Any use of memory location 0 is strictly forbidden, since it is owned by Motorola.  Trespassers may be shot on sight, but they may not die until much later.  Sometimes trespassers are only wounded and act strangely.  Any use of memory location 0 can be considered a bug, since there are no valid reasons for Macintosh programs to read or write to that memory.  However, nil pointers themselves are not necessarily bad.  It is occasionally necessary to pass nil pointers to ROM routines.  This should not be confused with reading or writing to memory location 0.  A pointer normally points to (contains the address of) a location in memory.  It could look like this:
  146. If a pointer has been cleared to nil, it will point to memory location 0.  This is OK as long as the program does not try to read from or write to that pointer.  An example of a nil pointer could look like:
  147. nil handles are related to the problem, since a handle is merely the address of a pointer (or a pointer to a pointer).  An example of what a normal handle might look like is:
  148. If the memory at 0 contains an odd number (numerically odd), then using it as a pointer will cause a system error with ID=2.  This can be very useful, since that tells you exactly where the program is using this illegal handle, making it easy to fix.  Unfortunately, there are cases where it is appropriate to pass a nil handle to ROM routines (such as GetScrap).  These cases are rare, and it is never legal to read from or write to a nil handle.
  149. There is also the case of an empty handle.  An empty handle is one where the handle itself (the first pointer) points to a valid place in memory; that place in memory is also a pointer, and if it is nil the entire handle is termed empty. There are occasions where it is necessary to use the handle itself, but using the nil pointer that it contains is not valid.  An example of an empty handle could be:
  150. Why it’s Bad
  151. The use of nil pointers can lead to the use of make-believe data.  This make-believe data often changes for different versions of the computer.  This changing data makes it difficult to predict what will happen when a program uses nil pointers.  Programs may not crash as a result of using a nil pointer, and they may behave in a consistent fashion.  This does not mean that there isn’t a bug.  This merely means that the program is lucky, and that it should be playing the lottery, not running on a Macintosh.  If a program acts differently on different versions of the Macintosh, you should think “could there be a nasty nil pointer problem here?”   Use of a nil handle usually culminates in reading or writing to obscure places in memory.  As an example:
  152.     VAR   myHandle:  TEHandle;
  153.     myHandle := nil;
  154. That’s pretty straightforward, so what’s the problem?  If you do something like:
  155.     myHandle^^.viewRect := myRect;  { very bad idea with myHandle = nil }
  156. memory location zero will be used as a pointer to give the address of a TextEdit record.  What if that memory location points to something in the system heap?  What if it points to the sound buffer?  In cases like these, eight bytes of rectangle data will be written to wherever memory location 0 points.  
  157. Use of a nil handle will never be useful.  This memory is reserved and used by the 68000 for various interrupt vectors and Valuable Stuff.  This Valuable Stuff is composed of things that you definitely do not want to change.  When changed, the 68000 finds out, and decides to get back at your program in the most strange and wonderful ways.  These strange results can range from a System Error all the way to erasing hard disks and destroying files.  There really is no limit to the havoc that can be wreaked.  This tends to keep the users on the edge of their seat, but this is not really the desired effect.  As noted above, it won’t necessarily cause traumatic results.  A program can be doing naughty things and not get caught.  This is still a bug that needs to be fixed, since it is nearly guaranteed to give different results on different versions of the Macintosh.  Programs exhibiting schizophrenia have been proven to be less enjoyable to use.
  158. How to avoid being a Niller
  159. Whenever a program uses pointers and handles, it should ensure that the pointer or handle will not be nil.  This could be termed defensive programming, since it assumes that everyone is out to get the program (which is not far from the truth on the Macintosh).  You should always check the result of routines that claim to pass back a handle. If they pass you back a nil handle, you could get in trouble if you use them.  Don’t trust the ROM.  The following example of a defensive use of a handle involves the Resource Manager.  The Resource Manager passes back a handle to the resource data.  There are any number of places where it may be forced to pass back a nil handle.  For example:
  160.     VAR   myRezzie:  MyHandle;
  161.     myRezzie := MyHandle(GetResource(myResType, myResNumber));    { could be                                            missing…}
  162.     IF myRezzie = nil  THEN  ErrorHandler('We almost got Nilled')
  163.     ELSE  myRezzie^^.myRect := newRect;       { We know it is OK }
  164. As another example, think of how handles can be purged from memory in tight memory conditions.  If a block is marked purgeable, the Memory Manager may throw it away at any time.  This creates an empty handle.  The defensive programmer will always make sure that the handles being used are not empty.  
  165.     VAR   myRezzie:  myHandle;
  166.     myRezzie := myHandle(GetResource(myResType, myResNumber));  { could be                                                                 missing… }
  167.     IF myRezzie = nil  THEN  ErrorHandler('We almost got Nilled')
  168.     ELSE  myRezzie^^.myRect := newRect;    { We know it is OK }
  169.     tempHandle := NewHandle (largeBlock);    {might dispose a purgeable                                  myRezzie}
  170.     IF myRezzie^ = nil  THEN LoadResource(Handle(myRezzie)); {Re-load empty                                                            handle}
  171.     IF ResError = noErr  THEN 
  172.         myRezzie^^.StatusField := OK;    { guaranteed not empty, and actually                                     gets read back in, if necessary }
  173. Be especially careful of places where memory is being allocated.  The NewHandle and NewPtr calls will return a nil handle or pointer if there is not enough memory.  If you use that handle or pointer without checking, you will be guilty of being a Niller.
  174. How to find Nillers
  175. The best way to find these nasty nil pointer problems is to set memory location zero to be an odd number (a good choice is 'NIL!' = $4E494C21, which is numerically odd, as well as personality-wise).  Please see OV 15 - Debugging Tips for details on how to do this.  
  176. If you use TMON, you can use the extended user area with Discipline.  Discipline will set memory location 0 to 'NIL!' to help catch those nasty pointer problems.  If you use Macsbug, just type SM 0 'NIL! and go.  Realize of course, that if a program has made a transgression and is actually using nil pointers, this may make the program crash with an ID=2 system error.  This is good!  This means that you have found a bug that may have been causing you untold grief.  Once you know where a program crashes, it is usually very easy to use a debugger to find where the error is in the source code.  When the program is compiled, turn on the debugging labels (usually a $D+ option).  Set memory location 0 to be 'NIL!'.  When the program crashes, look at where the program is executing and see what routine it was in (from a disassembly).  Go back to that routine in the source code and remove the offending code with a grim smile on your face.  Another scurvy bug has been vanquished.  The intoxicating smell of victory wafts around your head.
  177. Another way to find problems is to use a debugger to do a checksum on the first four bytes in memory (from 0 to 3 inclusive).  If the program ever traps into the debugger claiming that the memory changed, see which part of the program altered memory location 0.  Any code that writes to memory location zero is guilty of high treason against the state and must be removed.  Remember to say, “bugs are not my friends.”
  178. Creating or Using Fake Handles
  179. A fake handle is one that was not manufactured by the system, but was created by the program itself.  An example of a fake handle is:
  180.     CONST    aMem = $100;
  181.     VAR    myHandle: Handle;
  182.         myPointer: Ptr;
  183.     myPointer := Ptr (aMem);    { the address of some memory }
  184.     myHandle := @myPointer;    {the address of the pointer variable. Very bad.}
  185. The normal way to create and use handles is to call the Memory Manager NewHandle function. 
  186. Why it’s Bad
  187. A handle that is manufactured by the program is not a legitimate handle as far as the operating system is concerned.  Passing a fake handle to routines that use handles is a good way to discover the meaning of “Death by ROM.”  For example, think how confused the operating system would get if the fake handle were passed to DisposHandle.  What would it dispose?  It never allocated the memory, so how can it release it?  Programs that manufacture handles may find that the operating system is no longer their friend.
  188. When handles are passed to various ROM routines, there is no telling what sorts of things will be done to the handle.  There are any number of normal handle manipulation calls that the ROM may use, such as SetHandleSize, HLock, HNoPurge, MoveHHi and so on.  Since a program cannot guarantee that the ROM will not be doing things like this to handles that the program passes in, it is wise to make sure that a real handle is being used, so that all these type of operations will work as the ROM expects.  For fake handles, the calls like HLock and SetHandleSize have no bearing.  Fake handles are very easy to create, and they are very bad for the health of otherwise upstanding programs.  Whenever you need a handle, get one from the Memory Manager.  
  189. As a particularly bad use of a fake handle:
  190.     VAR   myHandle:  Handle;
  191.           myStuff:  myRecord;
  192.     myHandle := NewHandle (SIZEOF(myStuff));   { create a new normal handle }
  193.     myHandle^ := @myStuff;  {YOW!  Intended to make myHandle a handle to
  194.                     the myStuff record.  What it really does is
  195.                     blow up a Master Pointer block, Heap corruption,
  196.                     and death by Bad Heap.  Never do this. }
  197. This can be a little confusing, since it is fine to use your own pointers, but very bad to use your own handles.  The difference is that handles can move in memory, and pointers cannot, hence the pointers are not dangerous.  This does not mean you should use pointers for everything since that causes other problems.  It merely means that you have to be careful how you use the handles. 
  198. The use of fake handles usually causes system errors, but can be somewhat mysterious in its effects.  Fake handles can be particularly hard to track down since they often cause damage that is not uncovered for many minutes of use.  Any use of fake handles that causes the heap to be altered will usually crash the system.  Heap corruption is a common failure mode.  In clinical studies, 9 out of 10 programmers recommend uncorrupted heaps to their users who use heaps.
  199. How to avoid being a fakir
  200. The correct way to make a handle to some data is to make a copy of the data:
  201.     VAR   myHandle:  Handle;
  202.           myStuff:  myRecord;
  203.     errCode := PtrToHand (@myStuff, myHandle, SIZEOF(myStuff));
  204.     IF  errCode <> noErr  THEN ErrorHandler ('Out of memory');
  205. Always, always, let the Memory Manager perform operations with handles.  Never write code that assigns something to a master pointer, like:
  206.     VAR   myDeath:  Handle;
  207.     myDeath^ := stuff;  { Don’t change the Master pointer. }
  208. If there is code like this, it usually means the heap is being corrupted, or a fake handle is being used.  It is, however, OK to pass around the handle itself, like:
  209.     myCopyHandle := myHandle;   { perfectly OK, nobody will yell about this. }
  210. This is far different than using the ^ operator to accidentally modify things in the system.  Whenever it is necessary to write code to use handles, be careful.  Watch things carefully as they are being written.  It is much easier to be careful on the way in than it is to try to find out why something is crashing.  Be very careful of the @ operator.  This operator can unleash untold problems upon unsuspecting programs.  If at all possible, try to avoid using it, but if it is necessary, be absolutely sure you know what it is doing.  It is particularly dangerous since it turns off the normal type checking that can help you find errors (in Pascal).  In short, don’t get crazy with pointer and handle manipulations, and they won’t get crazy with you. 
  211. How to find fakirs
  212. Problems of this form are particularly insidious because it can be very difficult to find them after they have been created.  They tend to not crash immediately, but rather to crash sometime long after the real damage has been done.  The best way to find these problems is to run the program with Discipline.  (Discipline is a programmer’s tool that will check all parameters passed to the ROM to see if they are legitimate.  Discipline can be found as a stand-alone tool, but the most up-to-date version will be found in the Extended User Area for the TMON debugger.  The User Area is public domain, but TMON itself is not.  TMON has a number of other useful features, and is well worth the price.)  Discipline will check handles that are passed to the ROM to see if they are real handles or not, and if not, will stop the program at the offending call.  This can lead you back to the source at a point that may be close to where the bad handle was created.  If a program passes the Discipline test, it will be a healthy, robust program with drastically improved odds for compatibility.  Programs that do not pass Discipline can sleep poorly at night, knowing that they have broken at least one or two of the “rules.”   
  213. A way to find programs that are damaging the heap is to use a debugger (TMON or Macsbug) and turn on the Heap Check operation.  This will check the heap for errors at each trap call, and if the heap is corrupted will break into the debugger.  Hopefully this will be close to where the code is that caused the damage.  Unfortunately, it may not be close enough; this will force you to look further back.  
  214. Looking in the source code, look for all uses of the @ operator, and examine the code carefully to see if it is breaking the rules.  If it is, change it to step in line with the rest of the happy programs here in happy valley.  Also, look for any code that changes a master pointer like the myHandle^ := stuff.  Any code of this form is highly suspect, and probably a member of the Anti-Productivity League.  The APL has been accused of preventing software sales and the rise of the Yen.  These problems can be quite difficult to find at times, but don’t give up.  These fake handles are high on the list of guilty parties, and should never be trusted.  
  215. Writing code that modifies itself 
  216. Self-modifying code is software that changes itself.  Code that alters itself runs into two main groupings:  code that modifies the code itself and code that changes the block the code is stored in.  Copy protection code often modifies the code itself, to change the way it operates (concealing the meaning of what the code does).  Changing the code itself is very tricky, and also prone to having problems, particularly when the microprocessor itself changes.  There are third-party upgrades available that add a 68020 to a Macintosh.  Because of the 68020’s cache, programs that modify themselves stand a good chance of having problems when run on a 68020.  This is a compatibility point that should not be missed (nudge, nudge, wink, wink).  Code that changes other code (or itself) is prone to be incompatible when the microprocessor changes.  
  217. The second group is code that changes the block that the code is stored in.  Keeping variables in the CODE segment itself is an example of this.  This is uncommon with high-level languages, but it is easy to do in assembly language (using the DC directive).  Variables defined in the code itself should be read-only (constants).  Code that modifies itself has signed a tacit agreement that says “I’m being tricky, if I die, I’ll revise it.”
  218. Why it’s Bad
  219. There are now three different versions of the microprocessor, the 68000, 68010, and the 68020.  They are intended to be compatible with each other, but may not be compatible with code that modifies itself.  As the Macintosh evolves, the system may have compatibility problems with programs that try to “push the envelope.”
  220. How to avoid being an abuser
  221. Well, the obvious answer is to avoid writing self-modifying code.  If you feel obliged to write self-modifying code, then you are taking an oath to not complain when you break in the future.  But don’t worry about accidentally taking the oath: you won’t do it without knowing it.  If you choose to abuse, you also agree to personal visits from the Apple thought police, who will be hired as soon as we find out.
  222. How to find abusers
  223. Run the program on a 68020 system.  If it fails, it could be related to this problem, but since there are other bugs that might cause failures, it is not guaranteed to be a self-modifying code problem.  Self-modifying code is often used in copy protection, which brings us to the next big topic.
  224. Code designed strictly as copy protection
  225. Copy protection is used to make it difficult to make copies of a program.  The basic premise is to make it impossible to copy a program with the Finder.  This will not be a discussion as to the pros and cons of copy protection.  Everyone has an opinion.  This will be a description of reality, as it relates to compatibility.  
  226. Why it’s Bad
  227. System changes will never be made merely to cause copy protection schemes to fail, but given the choice between improving the system and making a copy protection scheme remain compatible, the system improvement will always be chosen.
  228. •    Copy protection is number one on the list of why programs fail the compatibility test.
  229. •    Copy protection by its very nature tends to do the most “illegal” things.  
  230. •    Programs that are copy protected are assumed to have signed a tacit agreement to revise the program when the system changes.  
  231. Copy protection itself is not necessarily bad.  What is bad is when programs that would otherwise be fully compatible do not work due only to the copy protection.  This is very sad, since it requires extra work, revisions to the software, and time lost while the revision is being produced.  The users are not generally humored when they can no longer use their programs.  Copy protection schemes that fail generally cause system errors when they are run.  They also can refuse to run when they should.  
  232. How to avoid being a protectionist
  233. The simple answer is to do without copy protection altogether.  If you think of compatibility as a probability game, if you leave out the copy protection, your odds of winning skyrocket.  As noted above, copy protection is the single biggest reason why programs fail on the various versions of the Macintosh.  For those who are required to use copy protection, try to rely on schemes that do not require specific hardware and make sure that the scheme used is not performing illegal operations.  If a program runs, an experienced Macintosh programmer armed with a debugger can probably make a copy of it, (no matter how sophisticated the copy protection scheme) so a moderate scheme that does not break the rules is probably a better compatibility bet.  The trickier and more devious the scheme, the higher the chance of breaking a rule.  Tread lightly.
  234. How to find protectionists
  235. The easiest way to see if a scheme is being overly tricky is to run it on a Macintosh XL.  Since the floppy disk hardware is different this will usually demonstrate an unwanted hardware dependency.  Be wary of schemes that don’t allow installation on a hard disk.  If the program cannot be installed on a hard disk, it may be relying upon things that are prone to change.  Don’t use schemes that access the hardware directly.  All Macintosh software should go through the various managers in the ROM to maintain compatibility.  Any code that sidesteps the ROM will be viewed as having said “It’s OK to make me revise myself.”  
  236. Check errors returned as function results
  237. All of the Operating System functions, as well as some of the Toolbox functions, will return result codes as the value of the function.  Don’t ignore these result codes.  If a program ignores the result codes, it is possible to have any number of bad things happen to the program.  The result code is there to tell the program that something went wrong; if the program ignores the fact that something is wrong, that program will probably be killed by whatever went wrong.  (Bugs do not like to be ignored.)  If a program checks errors, an anomaly can be nipped in the bud, before something really bizarre happens.
  238. Why it’s Bad
  239. A program that ignores result codes is skipping valuable information.  This information can often prevent a program from crashing and keep it from losing data.  
  240. How to avoid becoming a skipper
  241. Always write code that is defensive.  Assume that everyone and everything is out to kill you.  Trust no one.  An example of error checking is:
  242.     myRezzie := GetResource (myResType, myResId);
  243.     IF  myRezzie = nil  THEN  ErrorHandler ('Who stole my resource...');
  244. Another example:
  245.     fsErrCode := FSOpen ('MyFile', myVRefNum, myFileRefNum);
  246.     IF fsErrCode <> noErr  THEN ErrorHandler (fsErrCode, 'File error');
  247. And another:
  248.     myTPPrPort := PrOpenDoc (myTHPrint, nil, nil);
  249.     IF  PRError <> noErr  THEN  ErrorHandler (PRError, 'Printing error');
  250. Any use of Operating System functions should presume that something nasty can happen, and have code to handle the nasty situations.  Printing calls, File Manager calls, Resource Manager calls, and Memory Manager calls are all examples of Operating System functions that should be watched for returning errors.  Always, always check the result codes from Memory Manager calls.  Big memory machines are pretty common now, and it is easy to get cavalier about memory, but realize that someone will always want to run the program under Switcher, or on smaller Macintoshes.  It never hurts to check, and always hurts to ignore it.  
  251. How to find skippers
  252. This is easy: just do weird things while the program is running.  Put in locked or unformatted disks while the program is running.  Use unconventional command sequences.  Run out of disk space.  Run on 128K Macintoshes to see how the program deals with running out of memory.  Run under Switcher for the same reason.  (Programs that die while running under Switcher are often not Switcher’s fault, and are in fact due
  253.  to faulty memory management.)  Print with no printer connected to the Macintosh.   Pop disks out of the drives with the Command-Shift sequence, and see if the program can deal with no disk.  When a disk-switch dialog comes up, press Command-period to pass back an error to the requesting program (128K ROMs only).  Torturing otherwise well- behaved programs can be quite enjoyable, and a number of users enjoy torturing the program as much as the program enjoys torturing them.  For the truly malicious, run the debugger and alter error codes as they come back from various routines.  Sure it’s a dirty low-down rotten thing to do to a program, but we want to see how far we can push the program.  (This is also a good way to check your error handling.)  It’s one thing to be an optimist, but it’s quite another to assume that nothing will go wrong while a program is running. 
  254. Accessing hardware directly
  255. Sometimes it is necessary to go directly to the Macintosh hardware to accomplish a specific task for which there is no ROM support.  Early hard disks that used the serial ports had no ROM support.  Those disks needed to use the SCC chip (the 8530 communication chip) in a high-speed clocked fashion.  Although it is a valid function, it is not something that is supported in the ROM.  It was therefore necessary to go play with the SCC chip directly, setting and testing various hardware registers in the chip itself.  Another example of a valid function that has no ROM support is the use of the alternate video page for page-flipping animation.  Since there is no ROM call to flip pages, it is necessary to go play with the right bit in the VIA chip (6522 Versatile Interface Adapter).  Going directly to the hardware does not automatically throw a program into the incompatible group, but it certainly lowers its odds.
  256. Why it’s bad
  257. Going directly to the hardware poses any number of problems for enlightened programs that are trying to maintain compatibility across the various versions of the Macintosh.  On the Macintosh XL for example, a lot of the hardware is found in different locations, and in some cases the hardware doesn’t exist.  On the XL there is no sound chip.  Programs that go directly to the sound hardware will find they don’t work correctly on an XL.  If the same program were to go through the Sound Manager, it would work fine, although the sound would not be the same as expected.  Since the Macintosh is heavily oriented to the software side of things, expecting various hardware to always be available is not a safe bet.  Choosy programmers choose to leave the hardware to the ROM.  
  258. How to avoid having a hard attack
  259. Don’t read or write the hardware.  Exhaust every possible conventional approach before deciding to really get down and dirty.  If there is a Manager in the ROM for the operation you wish to perform, it is far better to use the Manager than to go directly to the hardware.  Compatibility at the hardware level can very rarely be maintained, but compatibility at the Manager level is a prime consideration.  If a program is down to the last ditch effort, and cannot get the support from the ROM that is desired, then access the hardware in an enlightened approach.  The really bad way to do it:
  260.     VIA := Pointer ($EFE1FE);  { sure it’s the base address today…}
  261.                         { This is bad.  Hard-coded number. }
  262. The with-it, inspired programmer of the eighties does something like:
  263.     TYPE LongPointer = ^LongInt;
  264.     VAR  VIA: LongPointer;
  265.          VIABase: LongInt;
  266.     VIA := Pointer ($1D4);  { the address of the low-memory global. }
  267.     VIABase := VIA^;        { get the low-memory variable’s value }
  268.                          { Now VIABase has the address of the chip }
  269. The point here is that the best way to get the address of a hardware chip is to ask the system where it currently is to be found.  The system always knows where the pieces of the system are, and will always know for every incarnation of the Macintosh.  There are low-memory global variables for all of the pieces of hardware currently found in the Macintosh.  This includes the VIA, the SCC, the Sound Chip, the IWM, and the video display.  Whenever you are stuck with going to the hardware, use the low-memory globals.  The fact that a program goes directly to the hardware means that it is risking imminent incompatibility, but using the low-memory global will ensure that the program has the best odds.  It’s like going to Las Vegas:  if you don’t gamble at all, you don’t lose any money; if you have to gamble, play the game that you lose the least on.
  270. How to find hard attacks
  271. Run the suspicious program on the Macintosh XL.  Nearly all of the hardware is in a different memory location on the XL.  If a program has a hard-coded hardware address in it, it will fail.  It may crash, or it might not perform the desired task, but it won’t work as advertised.  This unfortunately, is not a completely legitimate test, since the XL does not have some of the hardware of other Macintoshes, and some of the hardware that is there has the register mapping different.  This means that it is possible to play by the rule of using the low-memory global and still be incompatible.  
  272. Don’t use bits that are reserved
  273. Occasionally during the life of a Macintosh programmer, there comes a time when it is necessary to bite the bullet and use a low-memory global.  These are very sad days, since it has been demonstrated (by history) that low-memory global variables are a mysterious lot, and not altogether friendly.  One fellow in particular is known as ROM85, a word located at $28E.  This particular variable has been documented as the way to determine if a program is running on the 128K ROMs or not.  Notably, the top most bit of that word is the determining bit.  This means that the rest of the bits in that word are reserved, since nothing is described about any further bits.  Remember, if it doesn’t say, assume it’s reserved.  If it’s reserved, don’t depend upon it.  Take the cautious way out and assume that the other bits that aren’t documented are used for Switcher local variables, or something equally wild.  An example of a bad way to do the comparison is:
  274.     VAR  Rom85Ptr: WordPtr;
  275.          RomsAre64: Boolean;
  276.     Rom85Ptr := Pointer ($28E);    { point at the low-memory global }
  277.     IF  Rom85Ptr^ = $7FFF  THEN  RomsAre64 := False  { Bad test. }
  278.     ELSE  RomsAre64 := True;
  279. This is a bad test since the comparison is testing the value of all of the bits, not only the one that is valid.  Since the other bits are undocumented, it is impossible to know what they are used for.   Assume they are used for something that is arbitrarily random, and take the safe way out.
  280. How to avoid being bitten
  281.     VAR     ROM85Ptr: Ptr
  282.     Rom85Ptr := Pointer ($28E);    { point at the low-memory global }
  283.     IF BitTst(ROM85Ptr,0) THEN RomsAre64 := True {Good--tests only hi-bit}
  284.     ELSE  RomsAre64 := False;
  285. This technique will ensure that when those bits are documented, your program won’t be using them for the wrong things.  Beware of trojan bits.  
  286. Don’t use undocumented stuff.  Be very careful when you use anything out of the ordinary stream of a high-level language.  For instance, in the ROM85 case, it is very easy to make the mistake of checking for an absolute value instead of testing the actual bit that encodes the information.  Whenever a program is using low-memory globals, be sure that only the information desired is being used, and not some undocumented (and hence reserved) bits.  It’s not always easy to determine what is reserved and what isn’t, so conservative programmers always use as little as possible.  Be wary of the strange bits, and accept rides from none of them.  The ride you take might cause you to revise your program.
  287. How to find those bitten
  288. Since there are such a multitude of possible places to get killed, there is no simple way to see what programs are using illegal bits.  As time goes by it will be possible to find more of these cases by running on various versions of the Macintosh, but there will probably never be a comprehensive way of finding out who is accepting strange rides, and who is not.  Whenever the use of a bit changes from reserved status to active, it will be possible to find those bugs via extensive testing.  From a source level, it would be advisable to look over any use of low-memory globals, and eye them closely for inappropriate bit usage.  Do a global search for the $ (which describes those ubiquitous hexadecimal numbers), and when found see if the use of the number is appropriate.  Trust no one that is not known.  If they are documented, they will stay where they are, and have the same meaning.  Be very careful in realms that are undocumented.  Bits that suddenly jump from reserved to active status have been known to cause more than one program to have a sudden anxiety attack.  It is very unnerving to watch a program go from calm and reassuring to rabid status.  Users have been known to drop their keyboards in sudden shock (which is bad on the keyboards).   
  289. Summary
  290. So what does all this mean?  It means that it is getting harder and harder to get away with minor bugs in programs.  The minor bugs of yesterday are the major ones of today.  No one will yell at you for having bugs in your program, since all programs have bugs of one form or another.  The goal should be to make the programs run as smoothly and effortlessly as possible.  The end-users will never object to bug-reduced programs.
  291. What is the best way to test a program?  A reasonably comprehensive test is to exercise all of the program’s functions under the following situations:
  292. •    Use Discipline to be sure the program does not pass illegal things to the ROM.
  293. •    Use heap scramble and heap purge to be sure that handles are being used correctly, and that the memory management of the program is correct.
  294. •    Run with a checksum on memory locations 0...3 to see if the program writes to these locations.
  295. •    Run on a 128K Macintosh, or under Switcher with a small partition, to see how the program deals with memory-critical situations.
  296. •    Run on a 68020 system to see if the program is 68020-compatible and to make sure that changing system speed won’t confuse the program.
  297. •    Run on a Macintosh XL to be sure that the program does not assume too much about the operating system, and to test screen handling.
  298. •    Run on an Ultra-Large screen to be sure that the screen handling is correct, and that there are no hard-coded screen dimensions.
  299. •    Run on 64K ROM machines to be sure new traps are not being used when they don’t exist.
  300. •    Run under both HFS and MFS to be sure that the program deals with the file system correctly.  (400K floppies are usually MFS.)
  301. If a program can live through all of this with no Discipline traps, no checksum breaks, no system errors, no anomalies, no data loss and still get useful work done, then you deserve a gold medal for programming excellence.   Maybe even an extra medal for conduct above and beyond the call of duty.  In any case, you will know that you have done your job about as well as it can be done, with today’s version of the rules, and today’s programming tools.
  302. Sounds like a foreboding task, doesn’t it?  The engineers in Macintosh Technical Support are available to help you with compatibility issues (we won’t always be able to talk about new products, since we love our jobs, but we can give you some hints about compatibility with what the future holds).
  303. Good luck.
  304. Further Reference:
  305. •    Technical Note OV 3 - Compatibility Guidlines
  306. •    Technical Note OV 15 - Debugging Tips
  307. $HRˇ ˇˇˇˇRH†Ç
  308. /ZÅ#
  309.     0Ià:µú9"{    ˇˇˇˇˇˇˇˇ#†ƒ°d
  310. ONLNf˛†å°d1drw2…-·_ġˇˇˇˇˇè°ñ x°ddrw2:°ddrw2:$°d4drw2:°öˇ˙ó@†ò,Times
  311. .R…R…+]BNew Technical Notes†ô°ddrw2:°„†ó°d1drw2eÙġˇˇˇˇˇP°ñ x°ddrw2:°ddrw2:$°d4drw2:°öˇ˚ÄE¿†ò
  312. ({ïDeveloper Support†ô°ddrw2:°„†ó°d`drw2-ÔˇˇˇˇˇˇKÔ- Z  ffZ°d1drw2 ¿˙ÈˇˇˇˇˇˇK°ñ x°ddrw2:°ddrw2:$°d4drw2:0°öˇÙĆò
  313. 0(UÔ†ô°ddrw2:°„†ó°d1drw2ÔÊ˙ˇˇˇˇˇˇ°ñ x°ddrw2:°ddrw2:$°d4drw2:    °öˇ˝Ä†ò
  314.     +&    ®†ô°ddrw2:°„†ó°d1drw2Â-¯yˇˇˇˇˇˇ°ñ x°ddrw2:°ddrw2:$°d4drw2:°öˇ˚Ä%†ò
  315. (Z\    Macintosh†ô°ddrw2:°„†ó†ç°ddrw2D†É
  316. HR.°dONLNdçZ†}(õZOV 4 - Compatibility: Why & How
  317. °dONLNd üZÆö*Overview
  318. °dONLNd*∫ZΔí* Revised by:°dONLNd7∫„Δ(√„
  319. March 1988°dONLNdBΔZ“è(œZ Written by:°dONLNdNΔ¢“·)H Bob Johnson°dONLNdZΔ÷“(œ÷
  320. February 1987°dONLNdhfiZͬ(ÁZHWhile creating or revising any program for the Macintosh, you should be °dONLNd∞fi¬Í(Á¬aware of the most°dONLNd¬ÍZˆ’(ÛZLcommon reasons why programs fail on various versions of the Macintosh. This °dONLNdÍ’ˆ(Û’note will detail°dONLNdˆZ©(ˇZAsome common failure modes, why they occur, and how to avoid them. X°dONLNdaZ'ƒ*%OWe’ve tried to explain the issues in depth, but recognize that not everyone is °dONLNd∞ƒ'($ƒinterested in every°dONLNdƒ'Z3¿(0ZFissue. For example, if your application is not copy protected, you’re °dONLNd
  321. '¿3(0¿probably not very°dONLNd3Z?É(<Z?interested in the section on copy protection. That’s why we’ve °dONLNd[3É?(<É included the outline form of the°dONLNd|?ZKµ(HZKtechnical note. The first two pages outline the problems and the solutions °dONLNd«?µK(Hµthat are detailed later.°dONLNd‡KZW(TZ&Feel free to skip around at will, but °dONLNdKW)©8remember that we’re sending this enormous technical note°dONLNd?WZcfl(`Zbecause the suggestions it °dONLNdZWflc)Ö;provides may save you hasty compatibility revisions when we°dONLNdñcZo‘(lZannounce a new machine.°dONLNdÆ{ZáÒ*We know it’s a lot, and we’re °dONLNdÃ{Òá)ó<here to help you if you need it. Our address (electronic and°dONLNd    áZì#(êZ)physical) is on page two—contact us with °dONLNd2á#ì7)…any°dONLNd5á7ìÒ)& questions—that’s what we’re here for!
  322. °dONLNd\´Z∫(∑ZCompatibility: the outline
  323. °dONLNdwΔZ“7*'Don’t assume the screen is a fixed size°dONLNdü“Zfi¬* To get the screen size:°dONLNd∏fl~Î
  324. +$
  325. • check the QuickDraw global ,
  326. Courier°dONLNd’fi
  327. ÍÑ)èscreenBits.bounds°dONLNdÁ˜Z](Z.Don’t assume the screen is in a fixed location°dONLNdZ’* To get the screen location:°dONLNd3~
  328. +$
  329. • check the QuickDraw global °dONLNdP
  330. í)èscreenBits.baseAddr°dONLNdd)Z5ƒ(2ZDon’t assume that °dONLNdv(ƒ4)jrowBytes°dONLNd~)5…)@$ is equal to the width of the screen°dONLNd£5ZA(>Z%To get the number of bytes on a line:°dONLNd B~N
  331. +$
  332. • check the QuickDraw global °dONLNdÁA
  333. Mí)èscreenBits.rowBytes°dONLNd˚NZZÀ(WZTo get the screen width:°dONLNd[~g
  334. +$
  335. • check the QuickDraw global °dONLNd2Z
  336. fÆ)èscreenBits.bounds.right°dONLNdJgZsÍ(pZTo do screen-size calculations:°dONLNdkt~Äõ+$
  337. • Use °dONLNdqsõ”)LongInts°dONLNdzçZô˘(ñZDon’t write to or read from °dONLNdñå˘ò)ünil°dONLNdôçôW)  Handles or °dONLNd•åWòo)Fnil°dONLNd®çoô¢)     Pointers ¿X¿
  338. (’ZOV 4 - Compatibility: Why & How(’ˇ1) of 21(ÎZOverviewˇ°¿Ù%%DSIDICT:_cv
  339. currentdict /bu known {bu}if
  340. userdict /_cv known not{userdict /_cv 30 dict put}if
  341. _cv begin
  342. /bdf{bind def}bind def
  343. currentscreen/cs exch def/ca exch def/cf exch def
  344. /setcmykcolor where{/setcmykcolor get /cvcmyk exch def}{/cvcmyk{1 sub 4 1 roll 3{3 index add neg dup 0 lt{pop 0}if 3 1 roll}repeat setrgbcolor pop}bdf }ifelse
  345. /ss{//cf //ca //cs setscreen}bdf
  346. /stg{ss setgray}bdf
  347. /strgb{ss setrgbcolor}bdf
  348. /stcmyk{ss cvcmyk}bdf
  349. /min1{dup 0 eq{pop 1}if}bdf
  350. end
  351. currentdict /bn known {bn}if
  352. †ø ÜHRˇ ˇˇˇˇRH
  353. HR,Times
  354. .+6-Macintosh Technical Notes /4/˘
  355. °dONLNdH6Tˆ*$ Don’t create or Use Fake Handles°dONLNd!T6`Û* (To avoid creating or using fake handles:°dONLNdK`Zló+$ ?• Always let the Memory Manager perform operations with handles°dONLNdålZxz* =• Never write code that assigns something to a master pointer°dONLNd Ñ6ê(ç6%Don’t write code that modifies itself°dONLNdê6úm* BSelf modifying code will not live across incarnations of the 68000°dONLNd3®6¥ú*?Think carefully about code designed strictly as copy protection°dONLNds¥6¿"* 3To avoid copy protection-related incompatibilities:°dONLNd®¿ZÃ˚+$ "• Avoid copy protection altogether°dONLNdÃÃZÿ[* 6• Rely on schemes that don’t require specific hardware°dONLNdÿZ‰s* :• Make sure your scheme doesn’t perform illegal operations°dONLNd@6¸§(˘6Don’t ignore errors°dONLNdT¸6ª* To get valuable information:°dONLNdrZ+$ &• Check all pertinent calls for errors°dONLNdõZ È* • Always write defensive code°dONLNd∫,68Â(56Don’t access hardware directly°dONLNdŸ86D* ,To avoid hardware-related incompatibilities:°dONLNdDZP˚+$ "• Don’t read or write the hardware°dONLNd+PZ\‡* Q• If you can’t get the support from the ROM, ask the system where the hardware is°dONLNd~\ZhŸ* • Use low-memory globals°dONLNdót6ÄË(}6 Don’t use bits that are reserved°dONLNd∏Ä6åE* 8To avoid compatibility problems when bit status changes:°dONLNdÚåZò+$ • Don’t use undocumented stuff°dONLNdòZ§¨* A• When using low-memory globals, check only what you want to know°dONLNdT∞6ºk(π6Summary°dONLNd\»6‘K*:Minor bugs are getting harder and harder to get away with:°dONLNdò‘Z‡ì+$ • Good luck°dONLNd•‡ZÏí* • We’ll help°dONLNd≥ÏZ¯*  • AppleLink: MacDTS, MCI: MacDTS°dONLNd’¯Zó* >• U.S. Mail: 20525 Mariani Ave.; M/S 27-T; Cupertino, CA 95014
  356. °dONLNd6+x((6
  357. What it Is
  358. °dONLNd76CØ*PThe basic idea is to make sure that your programs will run, regardless of which °dONLNdo7ØC¯(@ØMacintosh they°dONLNd~C6Ov(L6Care being run on. The current systems to be concerned with include:°dONLNd√[Zg∞+$• Macintosh 128K°dONLNd‘[Δg!)l• Macintosh 512Ke°dONLNdÁgZs∞(pZ• Macintosh 512K°dONLNd¯gΔs)l• Macintosh Plus°dONLNd
  359. sZ•(|Z• Macintosh XL°dONLNdsΔ)l• Macintosh SE°dONLNd)Zãù(àZ• Macintosh II ¿4¿˘
  360. (’62) of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇ∂HRˇ ˇˇˇˇRH
  361. HR,Times
  362. .+Z-Developer Support Center(-Ï
  363. March 1988 /X/
  364. °dONLNd<ZHÌ(EZIf you perform operations in °dONLNd<ÌH)ì:a generic fashion, there is rarely any reason to know what°dONLNdXHZTÿ(QZOmachine is running. This means that you should avoid writing code to determine °dONLNdßHÿT(Qÿ
  365. which version°dONLNdµTZ`ù(]ZEof the machine you are running on, unless it is absolutely necessary.°dONLNd˚lZx‹*RFor the purposes of this discussion, the term “programs” will be used to describe °dONLNdMl‹x(u‹
  366. any code that°dONLNd[xZÑ~(ÅZ8runs on a Macintosh. This includes applications, INITs, °dONLNdìx~Ñ(Å~FKEYs, Desk Accessories and°dONLNdØÑZêÅ(çZDrivers.
  367. °dONLNd∏®Z∑Û*'What the “Rules” mean
  368. °dONLNdŒ√Zœ*YCompatibility across all Macintosh computers (which may sound like it involves more work °dONLNd'√œ(Ãfor°dONLNd+œZ€@(ÿZ/you) may actually mean that you have less work °dONLNdZœ@€)Ê.to do, since it may not be necessary to revise°dONLNdâ€ZÁû(‰Z
  369. your program °dONLNdñ€ûÁ)DLeach time Apple brings out a new computer or System file. Users, as a group,°dONLNd„ÁZÛ∂(ZKdo not understand compatibility problems; all they see is that the program °dONLNd.Á∂Û(∂does not run on their°dONLNdDÛZˇ(¸Zsystem.°dONLNdL Z`*5The benefits of being compatible are many-fold: your °dONLNdÅ `(`$customers/users stay happy, you have°dONLNd¶Z#’( Zless programming to do, °dONLNdæ’#){@you can devote your time to more valuable goals, there are fewer°dONLNdˇ#Z/ç(,ZBversions to deal with, your code will probably be more efficient, °dONLNdA#ç/(,çyour users will not curse you°dONLNd_/Z;è(8ZBunder their breath, and your outlook on life will be much merrier.°dONLNd¢GZS0**Now that we know what being compatible is °dONLNdÃG0S)÷1all about, recognize that nobody is requiring you°dONLNd˛SZ_(\ZXto be compatible with anything. Apple does not employ roving gangs of thought police to °dONLNdVS_(\be°dONLNdY_Zkƒ(hZsure that developers °dONLNdn_ƒk)j?are following the recommended guidelines. Furthermore, when the°dONLNdÆkZw&(tZ)guidelines comprise 1200 pages of turgid °dONLNd◊k&wI)Ãprose (°dONLNdfikIwú)#Inside Macintosh°dONLNdÓkúw)S), you can be expected to°dONLNdwZÉü(ÄZHmiss one or two of the “rules.” It is no sin to be incompatible, nor is °dONLNdPwüÉ(Äüit a punishable offense. If°dONLNdlÉZèe(åZit °dONLNdoÉeè) Swere, there would be no Macintosh programs, since virtually all developers would be°dONLNd√èZõh(òZ7incarcerated. What it does mean, however, is that your °dONLNd˙èhõ(òh"program will be unfavorably viewed°dONLNdõZߌ(§Zuntil it steps in line with °dONLNd9õŒß)tCthe current system (which is a moving target). If a program becomes°dONLNd}ßZ≥∑(∞ZFincompatible with a new Macintosh, it usually requires rethinking the °dONLNd√ß∑≥(∞∑offending code, and°dONLNd◊≥ZøY(ºZ0releasing a new version. You may read something °dONLNd≥Yø)ˇ&like “If the developers followed Apple°dONLNd.øZÀ(»Z%guidelines, they would be compatible °dONLNdSøÀ)∏5with the transverse-hinged diatomic quark realignment°dONLNdâÀZ◊ç(‘Z@system.” This means that if you made any mistakes (you read all °dONLNd…Àç◊(‘ç1200 pages carefully, right?),°dONLNdË◊Z„D(‡Z,you will not be compatible. It is extremely °dONLNd    ◊D„)Í*difficult to remain completely compatible,°dONLNd    ?„ZÔ≠(ÏZHparticularly in a system as complex as the Macintosh. The rules haven’t °dONLNd    á„≠Ô(Ï≠changed, but what you°dONLNd    ùÔZ˚î(¯Z>can get away with has. There are, however, a number of things °dONLNd    €Ôî˚(¯îthat you can do to improve°dONLNd    ˆ˚ZJ(Z/your odds—some of which will be explained here.°dONLNd
  370. &Z¥*It’s your choice°dONLNd
  371. 7+Z7É* It is still °dONLNd
  372. C+É7))Syour choice whether you will be concerned with compatibility or not. Apple will not°dONLNd
  373. ó7ZCJ(@Z3put out a warrant for your arrest. However, if you °dONLNd
  374.  7JC)/are doing things that are specifically illegal,°dONLNd
  375. ˙CZOn(LZ8Apple will also not worry about “breaking” your program.°dONLNd 3[Zgõ*
  376. Bad Things°dONLNd >sZ*&The following list is not intended to °dONLNd ds)´7be comprehensive, but these are the primary reasons why°dONLNd úZã“(àZIprograms break from one version of the system to the next. These are the °dONLNd “ã(à“current top ten°dONLNd ıãZóß(îZ
  377. commandments:°dONLNd £ZØ^*I°dONLNd £~Øa)$1Thou shalt not assume the screen is a fixed size. ¿X¿
  378. (’ZOV 4 - Compatibility: Why & How(’ˇ3) of 21(ÎZOverviewˇ¶HRˇ ˇˇˇˇRH
  379. HR,Times
  380. .+6-Macintosh Technical Notes /4/˘
  381. °dONLNd<6H>*II°dONLNd<ZH[)$8Thou shalt not assume the screen is at a fixed location.°dONLNd<I6UB(R6III°dONLNd@IZU€)$Thou shalt not assume that ,
  382. Courier°dONLNd[H€T)ÅrowBytes°dONLNdcIU∏)8% is equal to the width of the screen.°dONLNdâV6bB(_6IV°dONLNdåVZb¥)$Thou shalt not use °dONLNdüU¥a…)Znil°dONLNd¢V…b)  handles or °dONLNdÆUa)7nil°dONLNd±VbB)
  383.  pointers.°dONLNdºb6n>(k6V°dONLNdæbZn)$*Thou shalt not create or use fake handles.°dONLNdÈn6zB(w6VI°dONLNdÏnZz0)$/Thou shalt not write code that modifies itself.°dONLNdz6ÜF(É6VII°dONLNd zZܶ)$GThou shalt think twice about code designed strictly as copy protection.°dONLNdhÜ6íJ(è6VIII°dONLNdmÜZíP)$5Thou shalt check errors returned as function results.°dONLNd£í6ûC(õ6IX°dONLNd¶íZû)$(Thou shalt not access hardware directly.°dONLNdœû6™?(ß6X°dONLNd—ûZ™ƒ)$MThou shalt not use any of the bits that are reserved (unused means reserved).°dONLNd∂6¬¶(ø6MThis has been determined from extensive testing of our diverse software base.°dONLNdmŒ6⁄˛*#Assuming the screen is a fixed size°dONLNdëÊ6Ú"*1Do not assume that the Macintosh screen is 512 x °dONLNd¬Ê"Ú¯)Ï+342 pixels. Programs that do generally have°dONLNdÓÚ6˛Ê(˚6"problems on (or special case for) °dONLNdÚÊ˛¯)∞0the Macintosh XL, which has a wider screen. Most°dONLNdA˛6
  384. t(6Bapplications have to create the bounding rectangle where a window °dONLNdɲt
  385. ¯(tcan be dragged. This is the°dONLNdü
  386. 6|(6
  387. boundsRect°dONLNd© |Ò)F that is passed to the call:°dONLNd«#Z/~(,Z5DragWindow (myWindowPtr, theEvent.where, boundsRect);°dONLNd˝<6HÁ(E6%Some ill-advised programs create the °dONLNd";ÁG-)±
  388. boundsRect°dONLNd,<-Há)F by something like:
  389.     °dONLNdATZ_ö(\Z@SetRect (boundsRect, 0,0,342,512);  { oops, this is hard-coded…}
  390. °dONLNdÇj6vÅ(s6 Why it’s Bad°dONLNdèÇ6ém* This is bad °dONLNdõÇmé¨)7because it is °dONLNd©ǨéÀ)?never°dONLNdÆÇÀé¯)@ necessary to specifically put in the bounding rectangle for the°dONLNdÔé6öÃ(ó6screen. On a Macintosh XL for °dONLNd
  391. éÃö¯)ñ:example, the screen size is 760x364 (and sometimes 608x431°dONLNdHö6¶(£6,with alternate hardware). If a program uses °dONLNdtö¶¯)‰(the hard-coded 0,0,342,512 as a bounding°dONLNdù¶6≤‚(Ø6\rectangle, end users will not be able to move their windows past the fictitious boundary of °dONLNd˘¶‚≤¯(Ø‚512.°dONLNd˛≥6øÈ(º6&If something similar were done to the °dONLNd$≤Èæ/)≥
  392. GrowWindow°dONLNd.≥/øfl)F' call, it would make it impossible for °dONLNdU≥flø¯)∞users°dONLNd[ø6ÀÆ(»6Mto grow their window to fill the entire screen. (Always a saddening waste of °dONLNd®øÆÀ¯(»Ævaluable screen°dONLNd∏À6◊m(‘6
  393. real-estate.)°dONLNdΔ„6ÔŒ*OAssuming screen size makes it more difficult to use the program on Macintoshes °dONLNd„ŒÔ¯(ÏŒwith big°dONLNdÔ6˚À(¯6 screens, by making it difficult °dONLNd>ÔÀ˚¯)ï8to grow or move windows, or by drawing in strange places°dONLNdw˚6©(6Iwhere they should not be drawing (outside of windows). Consider the case °dONLNd¿˚©¯(©of running on a°dONLNd–6π(6OMacintosh equipped with one of the full page displays, or Ultra-Large screens. °dONLNdπ¯(π
  394. No one who°dONLNd*6ª(6Wpaid for a big screen wants to be restricted to using only the upper-left corner of it.°dONLNdÇ+67**)How to avoid becoming a screening fascist°dONLNd¨C6Ofl* Never hard code the numbers 512 °dONLNdÃCflO¯)©5and 342 for screen dimensions. You should avoid using°dONLNd    O6[ö(X6constants for system °dONLNd    Oö[¯)dIvalues that can change. Parameters like these are nearly always available°dONLNd    a[6gx(d6
  395. in a dynamic °dONLNd    n[xg¯)BLfashion. Programs should read the appropriate variables while the program is°dONLNd    ªg6s˝(p6+running (at run-time, not at compile time).°dONLNd    Á6ã:*4Here’s how smart programs get the screen dimensions:
  396.     °dONLNd
  397. óZ¢«+$IInitGraf(@thePort); { QuickDraw global variables have to be initialized.}
  398. °dONLNd
  399. h°Z≠`* … ¿4¿˘(’64) of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇÙHRˇ ˇˇˇˇRH
  400. HR,Times
  401. .+Z-Developer Support Center(-Ï
  402. March 1988 /X/,
  403. Courier
  404.     °dONLNd<~G‹(D~FboundsRect := screenBits.bounds;   { The Real way to get screen size },     Helvetica°dONLNdIF¢QÚ+$
  405. (                                        °dONLNdsF2Q6)ê  °dONLNduF6Q‡)"{ Use QuickDraw global variable. }
  406. °dONLNdò\Zhr(eZThis °dONLNdù\rh)Vis smart, because the program never has to know specifically what the numbers are. All°dONLNdÙhZt¡(qZNreferences to rectangles that need to be related to the screen (like the drag °dONLNdBh¡t(q¡and grow areas of°dONLNdTuZÅ√(~Zwindows) should use °dONLNdht√Ä:)iscreenBits.bounds°dONLNdyu:Å˙)w) to avoid worrying about the screen size.°dONLNd£çZôú(ñZFNote that this does not do anything remotely like assume that “if the °dONLNdÈçúô(ñúcomputer is not a standard°dONLNdôZ•ÿ(¢ZNMacintosh, then it must be an XL.” Special casing for the various versions of °dONLNdRôÿ•(¢ÿ
  407. the Macintosh°dONLNd`•Z±π(ÆZHhas always been suspicious at best; it is now grounds for breaking. (At °dONLNd®•π±(Æπleast with respect to°dONLNdæ±ZΩπ(∫Zscreen dimensions.)°dONLNd“…Z’'**By the way, remember to take into account °dONLNd¸…'’)Õ1the menu bar height when using this rectangle. On°dONLNd.÷Z‚y(flZ8128K ROMs (and later) you can use the low-memory global °dONLNdf’y·ø(fly
  408. mBarHeight°dONLNdp÷ø‚¬)F °dONLNdq÷¬‚¯) (a word at °dONLNd|’¯·)6$BAA°dONLNdÄ÷‚)).°dONLNdÉ‚ZÓ‡(ÎZBut since we didn’t provide °dONLNdü‚‡Ó)Ü<a low-memory global for the menu bar height in the 64K ROMs,°dONLNd‹ÔZ˚ˇ(¯Z#you’ll have to hard code it to 20 (°dONLNdˇÓˇ˙)•$14°dONLNdÔ˚Œ)'). (You’re not the only ones to forget °dONLNd)ÔŒ˚)∫the future holds°dONLNd:˚Zà(Z    changes.)°dONLNdDZx*1How to find fascist screenism in current programs°dONLNdv+Z7*$The easiest way is to exercise your °dONLNdö+7)Æ5program on one of the Ultra-Large screen Macintoshes.°dONLNd–7ZC∑(@ZIThere should be no restrictions on sizing or moving the windows, and all °dONLNd7∑C(@∑drawing should have°dONLNd-CZO>(LZ/no problems. If there are any anomalies in the °dONLNd\C>O)‰,program’s usage, there is probably a lurking°dONLNdâOZ[¥(XZproblem. Also, do °dONLNdõO¥[)ZJa global find in the source code to see if the numbers 512 or 342 occur in°dONLNdÊ[Zg¥(dZLthe program. If so, and if they are in reference to the screen, excise them.°dONLNd3sZG**Assuming the screen is at a fixed location°dONLNd^ãZó***Some programs use a fixed screen address, °dONLNdàã*ó)–2assuming that the screen location will be the same°dONLNdªóZ£(†ZXon various incarnations of the Macintosh. This is not the case. For example, the screen °dONLNdó£(†is°dONLNd§Z∞€(≠Zlocated at memory location °dONLNd1£€Ø)Å$1A700°dONLNd7§∞u)* on a 128K Macintosh, °dONLNdM§u∞Å)pat °dONLNdP£ÅØ´) $7A700°dONLNdV§´∞)* on a 512K Macintosh,°dONLNdl±ZΩe(∫Zat °dONLNdo∞eºè) $F8000°dONLNdu±èΩ)* on the Macintosh XL, and at °dONLNdí∞ºF)ç$FA700°dONLNdò±FΩ∂)* on the Macintosh Plus.°dONLNd∞…Z’•(“Z Why it’s Bad°dONLNdΩ·ZÌ*\When a program relies upon the screen being in a fixed location, Murphy’s Law dictates that °dONLNd·Ì(Ían°dONLNdÌZ˘˙(ˆZYunknowing user will run it upon a computer with the screen in a different location. This °dONLNduÌ˙˘(ˆ˙usually°dONLNd}˘Z(Z[causes the system to crash, since the offending program will write to memory that was used °dONLNdÿ˘(for°dONLNd‹Z‘(ZLsomething important. Programs that crash have been proven to be less useful °dONLNd    (‘(‘than those that°dONLNd    8Zw(Zdon’t.°dONLNd    ?)Z5$*"How to avoid being a base screener°dONLNd    bAZMÔ*#Suffice it to say that there is no °dONLNd    ÖAÔM)ï@way that the address of the screen will remain static, but there°dONLNd    ΔMZYò(VZ?are rare occasions where it is necessary to go directly to the °dONLNd
  409. MòY(Vòscreen memory. On these°dONLNd
  410. YZe≠(bZFoccasions, there are bad ways and not-as-bad ways to do it. A bad way:
  411.     °dONLNd
  412. eq~|“+$DmyScreenBase := Pointer ($7A700);  { not good.  Hard-coded number. }
  413. °dONLNd
  414. ™áZì±(êZA not-as-bad way:
  415.     °dONLNd
  416. Ωü~™õ+$9InitGraf(@thePort);   { do this only once in a program. }
  417. °dONLNd
  418. ¯©~µÑ* … ¿X¿(’ZOV 4 - Compatibility: Why & How(’ˇ5) of 21(ÎZOverviewˇ∫HRˇ ˇˇˇˇRH
  419. HR,Times
  420. .+6-Macintosh Technical Notes /4/˘,
  421. Courier
  422.     °dONLNd<ZGê+$>myScreenBase := screenBits.baseAddr;  { Good.  Always works. }°dONLNdAF~Q‚+$
  423.                     °dONLNdVFÍQÀ)l-      {Yet another QuickDraw global variable}
  424. °dONLNdÑ\6hè(e6HUsing the latter approach is guaranteed to work, since QuickDraw has to °dONLNdÃ\èh¯(eèknow where to draw,°dONLNd‡h6t(q6/and the operating system tells QuickDraw where °dONLNdht¯)Á+the screen can be found. When in doubt, ask°dONLNd;t6Ä”(}6SQuickDraw. This will work on Macintosh computers from now until forever, so if you °dONLNdét”į(}”use this°dONLNdóÄ6åË(â6Wapproach you won’t have to revise your program just because the screen moved in memory.°dONLNdÔò6§µ*NIf you have a program (such as an INIT) that cannot rely upon QuickDraw being °dONLNd=òµ§¯(°µinitialized (via°dONLNdN§6∞n(Æ6InitGraf°dONLNdV•n±®)8), then it is °dONLNdd•®±
  425. ):possible to use the °dONLNdx§
  426. ∞B)bScrnBase°dONLNdÄ•B±¯)8# low-memory global variable (a long°dONLNd§≤6æ_(ª6word at °dONLNd¨±_Ω{))$824°dONLNd∞≤{æ‡)). This method runs °dONLNdƒ≤‡æ¯)e6a distant second to asking QuickDraw, but is sometimes°dONLNd˚æ6 h(«6
  427. necessary.°dONLNd÷6‚—*How to find base screeners°dONLNd!Ó6˙ê*The easiest way to °dONLNd4Óê˙¯)ZIfind base screeners is to run the offending program on machines that have°dONLNd~˙6Ç(6different screen °dONLNdè˙ǯ)LLaddresses. If any addresses are being used in a base manner, the system will°dONLNd‹6À(6Pusually crash. The offending program may also occasionally refuse to draw. Some °dONLNd,À¯(Àprograms°dONLNd56¸(6*afflicted with this problem may also hang °dONLNd_¸¯)Δ0the computer (sometimes known as accessing funny°dONLNdê6+ó((6space). Also, do a °dONLNd£ó+æ)a8global find on the source code to look for numbers like °dONLNd€æ*Ë((æ$7A700°dONLNd·Ë+¯)* or°dONLNdÂ+67`(56$1A700°dONLNdÎ,`8ñ)*B. When found, exercise caution while altering the offending lines.°dONLNd.F6RÑ(O6:Assuming that rowbytes is equal to the width of the screen°dONLNdi_6kÃ*!According to the definition of a °dONLNdä^Ãjˆ)ñbitMap°dONLNdê_ˆk$)*
  428.  found in °dONLNdö_$ku).Inside Macintosh°dONLNd™_uk™)Q  (p I-144), °dONLNd∂_™k¯)5you can see that°dONLNd«k6wn(u6rowBytes°dONLNdœlnx|)8 is °dONLNd”l|x )Dthe number of actual bytes in memory that are used to determine the °dONLNdk wÙ(u bitMap°dONLNdlÙx¯)*.°dONLNdx6Ñ3(Å61We know the screen is just a big hunk of memory, °dONLNdPx3ѯ)˝$and we know that QuickDraw uses that°dONLNduÖ6ëu(é6 memory as a °dONLNdÅÑuêü)?bitMap°dONLNdáÖüë¶)*. °dONLNdâѶêfi)rowBytes°dONLNdëÖfiëu)8! accomplishes the translation of °dONLNd≤Öuë¯)óa big hunk of memory into°dONLNdÃí6û>(õ6a °dONLNdŒë>ùh)bitMap°dONLNd‘íhû¶)*. To do this, °dONLNd‚ë¶ùfi)>rowBytes°dONLNdÍífiû·)8 °dONLNdÎí·û¯)7tells the system how long a given row is in memory and,°dONLNd#û6™:(ß6/more importantly, where in memory the next row °dONLNdRû:™¯(ß:%starts. For conventional Macintoshes,°dONLNdx™6∂n(¥6rowBytes°dONLNdÄ´n∑¡)8 (bytes per Row) °dONLNdëÆ¡∫«+S*°dONLNdí´«∑»(¥«9 8 (Pixels per Byte) gives the final horizontal width of °dONLNdÀ´»∑¯(¥»
  429. the screen°dONLNd÷π6≈J(¬6<as Pixels per Row. This does not have to be the case. It is °dONLNdπJ≈¯(¬J#possible to have a Macintosh screen°dONLNd6Δ6“g(œ6
  430. where the °dONLNd@≈g—ü)1rowBytes°dONLNdHΔü“;)8! extends beyond what is actually °dONLNdiΔ;“¯)ú'visible on the screen. You can think of°dONLNdë”6fl    (‹6/it as having the screen looking in on a larger °dONLNd¿“    fi3)”bitMap°dONLNdΔ”3flÊ)*'. Diagrammatically, it might look like: ¿4¿˘
  431. (’66) of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇ˙HRˇ ˇˇˇˇRH
  432. HR,Times
  433. .+Z-Developer Support Center(-Ï
  434. March 1988 /X/†Ç†å
  435. <Ωπ
  436. 4LÚ∑8°ñ°öˇ˛†ò,
  437. Courier
  438.     À¸À¸°dONLNdˇˇ(≈    BaseAddr:†ô†ó°ñ°öˇ˛-†ò°dONLNdˇˇ(G(Big Hunk o’ Memory†ô†ó
  439.  @Ä4µÚg8°ñ2Â
  440. L°öˇ˛†ò°dONLNdˇˇ(Ì
  441. Visible Area†ô†ó°ñ2∞˝ª]°öˇ˛*†ò°dONLNdˇˇ(∏screenBits.Bounds†ô†ó†¨ad≠x¡ˆ0adÈx˝B0 n˙nƆ≠°ñ2h;si°öˇ˛†ò°dONLNdˇˇ(p>RowBytes†ô†ó†ç†É
  442. HR
  443. °dONLNdZ4(Z*With an Ultra-Large screen, the number of °dONLNd*4)⁄*bytes used for screen memory may be in the°dONLNdUZ+Ü((Z500,000 °dONLNd]Ü+),Qbyte range. Whenever calculations are being made to find various locations in the°dONLNdØ+Z7(4Zscreen, °dONLNd∑+7)%Vthe variables used should be able to handle larger screen sizes. For example, a 16 bit°dONLNd7ZCã(AZInteger°dONLNd8ãDz)13 will not be able to hold the 500,000 number, so a °dONLNdH7zC´)ÔLongInt°dONLNdO8´D)1 would be required.  °dONLNdd8D)cDo°dONLNdgEZQl(NZnot°dONLNdjElQf)4 assume that the screen size is 21,888 bytes long.  °dONLNdûDfPó)˙bitMaps°dONLNd•EóQõ)1 °dONLNd¶EõQÆ)can°dONLNd©EÆQ¯) be larger than °dONLNdπE¯Q)J32K or°dONLNd¿QZ]s(ZZ64K.°dONLNd≈iZu•* Why it’s Bad°dONLNd“ÅZç›*Programs that assume that °dONLNdÏÅ›ç)É@all of the bytes in a row are visible may make bad calculations,°dONLNd-çZô;(ñZ-causing drawing routines to produce unusual, °dONLNdZç;ô)·-and unreadable, results.  Also, programs that°dONLNdàöZ¶~(£Zuse the °dONLNdêô~•∂)$rowBytes°dONLNdòö∂¶C)8  to figure out the width of the °dONLNd∏öC¶)ç0screen rectangle will find that their calculated°dONLNdÈßZ≥§(∞Zrectangle is not °dONLNd˙ß§≥À)J    the real °dONLNd¶À≤B)'screenBits.Bounds°dONLNdßB≥)w/.  Drawing into areas that are not visible will°dONLNdD≥Zø;(ºZ0not necessarily crash the computer, but it will °dONLNdt≥;ø)·-probably give erroneous results, and displays°dONLNd¢øZÀI(»Z2that don’t match the normal output of the program.°dONLNd’◊Z„≈*Programs that assume °dONLNdÍ◊≈„)kEthat the number of bytes in the screen memory will be less than 32768°dONLNd0„ZÔŒ(ÏZMmay have problems drawing into Ultra-Large screens, since those screens will °dONLNd}„ŒÔ(ÏŒoften have more°dONLNdçÔZ˚»(¯ZBmemory than a normal Macintosh screen.  These particular problems °dONLNdœÔ»˚(¯»do not evidence°dONLNdfl˚Z†(Zthemselves by °dONLNdÌ˚†)FOcrashing the system.  They generally appear as loss of functionality (not being°dONLNd=Z¿(ZKable to move a window to the bottom of the screen), or as drawing routines °dONLNdà¿(¿that no longer look°dONLNdúZ˙(ZTcorrect.  These problems can prevent an otherwise wonderful program from being used.°dONLNdÒ+Z7*How to avoid being a row byter°dONLNdDZP *In any calculations, the °dONLNd)C O)prowBytes°dONLNd1DPí)8 variable should be thought of °dONLNdPDíP)êas the way to get to the next°dONLNdnPZ\µ(YZrow on the screen. °dONLNdÅPµ\)[L This is distinct from thinking of it as the width of the screen.  The width°dONLNdŒ]Zit(fZshould always be found from °dONLNdÍ\th(ftscreenBits.bounds.right–°dONLNdiZuÙ(sZscreenBits.bounds.left°dONLNdjÙv¯)ö.°dONLNdÇZé(ãZVIt is also inappropriate to use the rectangle to decide how many bytes there are on a °dONLNdtÇé(ãrow.°dONLNdzéZöˆ(óZ Programs that do something like:
  444.     °dONLNdú¶~±»+$BbytesLine := screenBits.bounds.right DIV 8;  { bad use of bounds } ¿X¿
  445. (’ZOV 4 - Compatibility: Why & How(’ˇ7) of 21(ÎZOverviewˇ
  446. HRˇ ˇˇˇˇRH
  447. HR,Times
  448. .+6-Macintosh Technical Notes /4/˘,
  449. Courier
  450.     °dONLNd<ZGˇ+$!rightSide := screenBits.rowBytes °dONLNd!?ˇJ+•*°dONLNd"<GÆ(D" 8;        { bad use of rowBytes }
  451. °dONLNdEU6aÚ(^6(will find that the screen may have more °dONLNdmTÚ`*)ºrowBytes°dONLNduU*aD)8 than °dONLNd{UDa¯)$previously thought.  The best way to°dONLNd†a6m«(j6Wavoid being a row byter is to use the proper variables for the proper things.  Without °dONLNd˜a«m¯(j«
  452. the proper°dONLNdm6y‚(v6[mathematical basis to the screen, life becomes much more difficult.  Always do things like:
  453.     °dONLNd_ÖZêÆ+$DbytesLine := screenBits.rowBytes;      { always the correct number }°dONLNd•èZö«*
  454. IrightSide := screenBits.bounds.right;  { always the correct screen size }
  455. °dONLNdÔ•6±«(Æ6It is sometimes necessary to °dONLNd •«±¯)ë<do calculations involving the screen.  If so, be sure to use°dONLNdI±6Ωn(ª6LongInts°dONLNdQ≤næ¯)8 for all the math, and be sure °dONLNdp≤¯æü)ä%to use the right variables (i.e. use °dONLNdï±üΩ◊)ßLongInts°dONLNdù≤◊æ¯)8).  For°dONLNd•¿6Ã"(…62example, if we need to find the address of the 500°dONLNd◊Ω"…+(Δ"th°dONLNdŸ¿+Ãı+    , row in the screen (500 lines from the top):
  456.     °dONLNdÿ6„E(‡6VAR°dONLNd
  457. ÿZ„å)$
  458. myAddress:°dONLNdÿ¢„ )HLongInt;°dONLNd‚ZÌx(ÍZmyRow:°dONLNd&‚~̶)$LongInt;°dONLNd0‚ÍÌ≤)l({ so the calculations don’t round off. }°dONLNdZÏZ˜á(ÙZ    myOffset:°dONLNddÏ¢˜ )HLongInt;°dONLNdmÏ͘î)H"{ could easily be over 32768 ... }°dONLNdëˆZå(˛Z
  459. bytesLine:°dONLNdúˆ¢ )HLongInt;°dONLNd¶Z Ω(ZGmyAddress := ord4(screenBits.baseAddr);{start w/the real base address }°dONLNdÔ
  460. Z™*
  461. myRow := 500;   °dONLNd
  462. Æ)¥    {the row we want to address }°dONLNd$Zü(ZAbytesLine := screenBits.rowBytes;      {the real bytes per line }°dONLNdgZ)*
  463. "myOffset := myRow * bytesLine;    °dONLNdä)€)¥)   {lines * bytes per lines gives bytes }°dONLNdµ*Z5(2Z"myAddress := myAddress + myOffset;°dONLNdÿ*5ö)¥   {final address of the 500°dONLNdÙ'ö2§(/öth°dONLNdˆ*§5«+
  464.  line }
  465. °dONLNd˛@6L¥(I6This is not something you °dONLNd@¥L¯)~Bwant to do if you can possibly avoid it, but if you simply must go°dONLNd[L6X›(U6%directly to the screen, be careful.  °dONLNdÄL›X¯)ß8The big-screen machines (Ultra-Large screens) will thank°dONLNdπX6de(a6:you for it.  If QuickDraw cannot be initialized, there is °dONLNdÛXed¯(aealso the low-memory global°dONLNdd6pu(n6    screenRow°dONLNdeuq´)?  (a word at °dONLNd#d´p«)6$106°dONLNd'e«qX)!) that will give you the current °dONLNdHdXpê)ërowBytes°dONLNdPeêqî)8.°dONLNdT}6âº(Ü6How to find row byters°dONLNdkï6°G*To °dONLNdnïG°¯)Rfind current problems with row byter programs, run them on a machine equipped with°dONLNd¡°6≠$(™62Ultra-Large screens and see if any anomalies crop °dONLNdÛ°$≠¯)Ó*up.  Look for drawing sequences that don’t°dONLNd≠6π7(∂67work right, and for drawing that clips to an imaginary °dONLNdU≠7π¯(∂7(edge.  For source-level inspection, look°dONLNd~∫6Δ_(√6    for uses °dONLNdá∫_Δ))of the °dONLNdéπ≈∑) rowBytes°dONLNdñ∫∑Δ¯)8C variables and be sure that they are being used in a mathematically°dONLNd⁄«6”É(–6sound fashion.  °dONLNdͫɔW)M+Be highly suspicious of any code that uses °dONLNdΔW“è)‘rowBytes°dONLNd«è”¯)8 for the screen width.°dONLNd5”6fl¸(‹6(Any calculations involving those system °dONLNd]”¸fl¯)Δ3variables should be closely inspected for round-off°dONLNdëfl6ÎA(Ë6:errors and improper use.  Search for the number 8.  If it °dONLNdÀflAί(ËA'is being used in a calculation where it°dONLNdÛÎ6˜≤(Ù6is the number of bits per °dONLNd    
  466. Î≤˜¯)|Bbyte, then watch that code closely for improper conceptualization.°dONLNd    Q˜6∏(6TThis is code that could leap out and grab you by the throat at anytime.  Be careful!°dONLNd    ®6(\*%Using °dONLNd    Æ\'t)&nil°dONLNd    ±t(∫)  Handles or °dONLNd    Ω∫'“)Fnil°dONLNd    ¿“()     Pointers°dONLNd     56AA(>6A °dONLNd    Ã4A@V) nil°dONLNd    œ5VA„)! pointer is a pointer that has a °dONLNd    5„A¯)ç9value of 0.  Recognize that pointers are merely addresses°dONLNd
  467. *B6Nœ(K6in memory.  This means that a °dONLNd
  468. HAœM‰)ônil°dONLNd
  469. KB‰N¯)6 pointer is pointing to memory location 0.  Any use of°dONLNd
  470. ÇN6Zó(W6Imemory location 0 is strictly forbidden, since it is owned by Motorola.  °dONLNd
  471. ÀNóZ¯(WóTrespassers may be°dONLNd
  472. fiZ6f-(c66shot on sight, but they may not die until much later. °dONLNd Z-f¯)˜' Sometimes trespassers are only wounded°dONLNd <f6r¶(o6and act strangely.  Any °dONLNd Tf¶r¯)pDuse of memory location 0 can be considered a bug, since there are no°dONLNd ôs6{(|6valid reasons °dONLNd ßs{„)EBfor Macintosh programs to read or write to that memory.  However, °dONLNd Èr„~¯(|„nil°dONLNd ÌÄ6å(â6-pointers themselves are not necessarily bad. °dONLNd Äåπ)“& It is occasionally necessary to pass °dONLNd @πãŒ)±nil°dONLNd CÄŒå¯)     pointers°dONLNd Må6òÍ(ï6%to ROM routines.  This should not be °dONLNd råÍò¯)¥6confused with reading or writing to memory location 0.°dONLNd ™ò6§e(°6BA pointer normally points to (contains the address of) a location °dONLNd Ïòe§¯(°ein memory.  It could look like°dONLNd
  473. §6∞J(≠6this: ¿4¿˘
  474. *(8) of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇ¬HRˇ ˇˇˇˇRH
  475. HR,Times
  476. .+Z-Developer Support Center(-Ï
  477. March 1988 /X/†Ç†å°ñ°ö*R†ò
  478. Hu,
  479. Courier
  480.     ÃãÃã°dONLNdˇˇ(UYThis is how a Pointer
  481. †ô°öR†ò°dONLNdˇˇ* works.  The address of
  482. †ô°öR†ò°dONLNdˇˇ* the pointer variable itself
  483. †ô°ö    R†ò°dONLNdˇˇ* is $E9310 (@P) and is four
  484. †ô°öˇ˛R†ò°dONLNdˇˇ*  bytes long.  The pointer points
  485. †ô°öˇÛR†ò°dONLNdˇˇ* to (contains the address of)
  486. †ô°öˇËR†ò°dONLNdˇˇ* the block at $3E4DE (P).
  487. †ô°öˇ›R†ò°dONLNdˇˇ* That memory location is where
  488. †ô°öˇ“R†ò°dONLNdˇˇ* the actual data resides (P^).†ô†ó
  489. 4K”8
  490. ™U™U™U™U4›”8°ñ°öˇ˛†ò°dONLNdˇˇ(õMemory 0†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(ùÇ
  491. Higher Memory†ô†ó°ñ°öˇ˛!†ò°dONLNdˇˇ(T}Highest Memory†ô†ó
  492. 4`”l8°ñ°öˇ˛†ò°dONLNdˇˇ+l$3E4DE†ô†ó
  493. Ä4©”Œ8°ñ2ØÍ∫°ö
  494. †ò°dONLNdˇˇ+OReal
  495. †ô2∫Í≈°öˇ¯
  496. †ò°dONLNdˇˇ* Data†ô†ó"e)"»Eù†™a∫ÿ0B0"»EÁ†≠°ñ°öˇ˛†ò°dONLNdˇˇ(h°
  497. P: $E9310:†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ( ú P^: $3E4DE:†ô†ó†ç†É
  498. HR
  499. °dONLNd!Z-Ì(*Z!If a pointer has been cleared to °dONLNd! Ì,)ìnil°dONLNd$!-≠)&, it will point to memory location 0. °dONLNdJ!≠-)´ This is OK as long as°dONLNda.Z:k(7Zthe °dONLNde.k:‚)Mprogram does not try to read from or write to that pointer.  An example of a °dONLNd≤-‚9˜(7‚nil°dONLNdµ.˜:) pointer°dONLNdæ:ZF£(CZcould look like:†Ç†å
  500. Ru)
  501. 4U”8
  502. ™U™U™U™U4Á” 8°ñ°ö†ò
  503.     ◊ã◊ã°dONLNdˇˇ+K’    Memory 0 †ô°öˇ¯ˇ˚†ò°dONLNdˇˇ+ (P^)†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(ßÇ
  504. Higher Memory†ô†ó°ñ°öˇ˛!†ò°dONLNdˇˇ(^}Highest Memory†ô†ó
  505. 4j”v8°ñ°öˇ˛†ò°dONLNdˇˇ+x0†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(‘∞$3E4DE:†ô†ó
  506. Ä4≥”ÿ8°ñ2πÓƒ °ö
  507. †ò°dONLNdˇˇ(¡ÛReal
  508. †ô2ƒÓœ °öˇ¯
  509. †ò°dONLNdˇˇ* Data†ô†ó"o) EoE†™a&2B0"EȆ≠°ñ°ö$J†ò°dONLNdˇˇ(ehThis is a nil Pointer.
  510. †ô°öJ†ò°dONLNdˇˇ* Note that the memory that
  511. †ô°öJ†ò°dONLNdˇˇ* it points to (the address)
  512. †ô°öJ†ò°dONLNdˇˇ* is 0 (P^).  This is wrong.  
  513. †ô°öˇ¯J†ò°dONLNdˇˇ* There is no valid data at 
  514. †ô°öˇÌJ†ò°dONLNdˇˇ* memory location 0.  Any †ô°öˇ‚J†ò°dONLNdˇˇ* writing to or reading from †ô°öˇ◊J†ò°dONLNdˇˇ* this pointer is a bug.†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(r°
  515. P: $E9310:†ô†ó†ç†É
  516. HR
  517. °dONLNdœ5ZAfi(>ZRnil handles are related to the problem, since a handle is merely the address of a °dONLNd!5fiA(>fi
  518. pointer (or a°dONLNd/AZMΔ(JZNpointer to a pointer).  An example of what a normal handle might look like is: ¿X¿
  519. *ãOV 4 - Compatibility: Why & How(’ˇ9) of 21(ÎZOverviewˇΔHRˇ ˇˇˇˇRH
  520. HR,Times
  521. .+6-Macintosh Technical Notes /4/˘†Ç†å
  522. <;Û
  523. 4?ô‚8
  524. ™U™U™U™U4—ôˆ‚8
  525. 4ÿô‰‚8°ñ°öˇ˛†ò,
  526. Courier
  527.     Ã∏Ã∏°dONLNdˇˇ++’Memory 0†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(ëH
  528. Higher Memory†ô†ó°ñ°öˇ˛!†ò°dONLNdˇˇ(HCHighest Memory†ô†ó4Tô`‚8°ñ°öˇ˛†ò°dONLNdˇˇ+l$2603C†ô†ó
  529. Ä4ùô¬‚8°ñ2£∞ÆŒ°ö
  530. †ò°dONLNdˇˇ+OReal
  531. †ô2Æ∞πŒ°öˇ¯
  532. †ò°dONLNdˇˇ* Data†ô†ó"Y‚; ›[†™aœÚÌB0"›†≠"fi·"Ω˘!†™aØ’ÕÛB0"Ω˜¯†≠°ñ°ö@^†ò°dONLNdˇˇ(J3This is how a Handle works.
  533. †ô°ö5^†ò°dONLNdˇˇ* The address of the handle
  534. †ô°ö*^†ò°dONLNdˇˇ* variable itself (H) is $E9310.
  535. †ô°ö^†ò°dONLNdˇˇ* That variable points (has the
  536. †ô°ö^†ò°dONLNdˇˇ* address) to the master pointer
  537. †ô°ö    ^†ò°dONLNdˇˇ* at location $2603C (H).  That
  538. †ô°öˇ˛^†ò°dONLNdˇˇ*  variable is a pointer also, and
  539. †ô°öˇÛ^†ò°dONLNdˇˇ* points to the real data found
  540. †ô°öˇË^†ò°dONLNdˇˇ* !at $3E4DE (H^^).  The dark grey 
  541. †ô°öˇ›^†ò°dONLNdˇˇ* "block is a Master pointer block. I°dONLNdˇˇ)™t
  542. †ô°öˇ“^†ò°dONLNdˇˇ(∏3"is a group (usually 64) of Master
  543. †ô°öˇ«^†ò°dONLNdˇˇ* "Pointers.  One of them is the Mast°dONLNdˇˇ)™er
  544. †ô°öˇº^†ò°dONLNdˇˇ(Œ3Pointer at address $2603C (H^).†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(‡Æ$3E4DE†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(\g
  545. H: $E9310:†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(æ] H^^: $3E4DE:†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ+" H^: $2603C:†ô†ó†ç†É†Ç†å°ñ°ö$a†ò
  546. 9ÍÙ÷ª÷ª°dONLNdˇˇ+ÕBThis is a nil Handle.
  547. †ô°öa†ò°dONLNdˇˇ* Note that the Handle usually
  548. †ô°öa†ò°dONLNdˇˇ*  points to a Master Pointer, but
  549. †ô°öa†ò°dONLNdˇˇ* in this case it points at (has
  550. †ô°öˇ¯a†ò°dONLNdˇˇ* "the value of) 0 (H^).  This is wro°dONLNdˇˇ)™ng.
  551. †ô°öˇÌa†ò°dONLNdˇˇ(Y/!Using what is at memory location
  552. †ô°öˇ‚a†ò°dONLNdˇˇ* !0 as a pointer is invalid, since
  553. †ô°öˇ◊a†ò°dONLNdˇˇ* "it is not known what will be there°dONLNdˇˇ)™.†ô†ó"Ÿfl†™aë>Ø\ˆ0"ü2 †≠°ñ°öˇ˛R†ò°dONLNdˇˇ(§M H^^: Points someplace strange...†ô†ó
  554. 4ò›‡8
  555. ™U™U™U™U4®òÕ‡8
  556. 4ØòΩ‡8°ñ°ö†ò°dONLNdˇˇ(Ÿd    Memory 0 †ô°öˇ¯ˇ˚†ò°dONLNdˇˇ+ (H^)†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(hF
  557. Higher Memory†ô†ó°ñ°öˇ˛!†ò°dONLNdˇˇ(AHighest Memory†ô†ó4+ò7‡8°ñ°öˇ˛†ò°dONLNdˇˇ+x0†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(ït$3E4DE:†ô†ó
  558. Ä4tòô‡8°ñ°öˇ˛†ò°dONLNdˇˇ*"$2603C:†ô†ó°ñ2z∞ÖŒ°ö
  559. †ò°dONLNdˇˇ(ǵReal
  560. †ô2Ö∞ꌰöˇ¯
  561. †ò°dONLNdˇˇ* Data†ô†ó"0‡; Ÿ2†™aÀÚÈB0"ŸÒ†≠"µfl"ì˜"†™aÖ‘£ÚB0"ìı˘†≠°ñ°öˇ˛†ò°dONLNdˇˇ(∑¨$3E4DE†ô†ó"⁄¯9Δ°ñ°öˇ˛†ò°dONLNdˇˇ(3e
  562. H: $E9310:†ô†ó†ç†É
  563. HR
  564. °dONLNdˆ6Ê(ˇ6XIf the memory at 0 contains an odd number (numerically odd), then using it as a pointer °dONLNdXˆÊ¯(ˇÊwill°dONLNd]6é( 6Kcause a system error with ID=2.  This can be very useful, since that tells °dONLNd®é¯( éyou exactly where the°dONLNdæ6À(6Sprogram is using this illegal handle, making it easy to fix.  Unfortunately, there °dONLNdÀ¯(À    are cases°dONLNd6'’($6"where it is appropriate to pass a °dONLNd=’&Í)ünil°dONLNd@Í'í)! handle to ROM routines (such as °dONLNdaí& )®GetScrap°dONLNdi '¯)8    ).  These°dONLNds(64§(16cases are rare, and it is °dONLNdç(§4√)nnever°dONLNdí(√4X)" legal to read from or write to a °dONLNd¥'X3m)ïnil°dONLNd∑(m4ì) handle.°dONLNd¿@6LT(I6<There is also the case of an empty handle.  An empty handle °dONLNd¸@TL¯(IT#is one where the handle itself (the°dONLNd L6XE(U6=first pointer) points to a valid place in memory; that place °dONLNd]LEX¯(UE&in memory is also a pointer, and if it°dONLNdÑY6eA(b6is °dONLNdáXAdV) nil°dONLNdäYVe¥) the entire handle is °dONLNd†Y¥e¯)^Btermed empty. There are occasions where it is necessary to use the°dONLNd„f6r•(o6handle itself, but using °dONLNd¸f•r∑)othe °dONLNde∑qÃ)nil°dONLNdfÃr¯)? pointer that it contains is not valid.  An example of an empty°dONLNdCr6~É({6handle could be: ¿4¿˘
  565. *Z10)
  566.  of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇäHRˇ ˇˇˇˇRH
  567. HR,Times
  568. .+Z-Developer Support Center(-Ï
  569. March 1988 /X/†Ç†å°ñ°ö/T†ò
  570. <d,
  571. Courier
  572.     ÿÆÿưdONLNdˇˇ(GfThis is an Empty Handle.
  573. †ô°ö$T†ò°dONLNdˇˇ* Note that the handle itself
  574. †ô°öT†ò°dONLNdˇˇ* has a valid Master Pointer
  575. †ô°öT†ò°dONLNdˇˇ*  address in it $2603C (H^).  The
  576. †ô°öT†ò°dONLNdˇˇ* Master Pointer is nil however,
  577. †ô°öˇ¯T†ò°dONLNdˇˇ* !which is the address of location
  578. †ô°öˇÌT†ò°dONLNdˇˇ* !0 in memory.  It is wrong to use
  579. †ô°öˇ‚T†ò°dONLNdˇˇ* !the Master Pointer in this case,
  580. †ô°öˇ◊T†ò°dONLNdˇˇ* although there are cases where
  581. †ô°öˇÃT†ò°dONLNdˇˇ* !using the Handle itself is valid.†ô†ó
  582. 4A¬ 8
  583. ™U™U™U™U4“¬˜ 8
  584. 4Ÿ¬Â 8°ñ°ö†ò°dONLNdˇˇ(è    Memory 0 †ô°öˇ¯†ò°dONLNdˇˇ+ (H^^)†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(íq
  585. Higher Memory†ô†ó°ñ°öˇ˛!†ò°dONLNdˇˇ(IlHighest Memory†ô†ó4U¬a 8
  586. Ä4û¬√ 8"Z ; fiF\F†™a–Ó=B0"fiFÛ†≠°ñ°öˇ˛†ò°dONLNdˇˇ+wò0†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(]◊$2603C†ô†ó"fi
  587. ##†™aÛB0#Ù†≠°ñ2•÷∞˛°ö†ò°dONLNdˇˇ+PPurged
  588. †ô2∞÷ª˛°öˇ¯
  589. †ò°dONLNdˇˇ+ Data†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(]ê
  590. H: $E9310:†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ+b$3E4DE:†ô†ó°ñ°öˇ˛†ò°dONLNdˇˇ(·ã H^: $2603C:†ô†ó†ç†É
  591. HR
  592. °dONLNd Z,•()Z Why it’s Bad°dONLNd
  593. 9ZEè* The use of °dONLNd8èD§)5nil°dONLNd9§EÌ)G pointers can lead to the use of make-believe data.  This make-believe °dONLNdb9ÌE(BÌ
  594. data often°dONLNdmEZQö(NZDchanges for different versions of the computer.  This changing data °dONLNd±EöQ(Nömakes it difficult to predict°dONLNdœRZ^”([Zwhat will happen when a °dONLNdÁR”^)y
  595. program uses °dONLNdÙQ]-)Enil°dONLNd˜R-^)1 pointers.  Programs may not crash as a result of°dONLNd)_ZkÅ(hZusing a °dONLNd1^Åjñ)'nil°dONLNd4_ñkæ)= pointer, and they may behave in a consistent fashion.  This °dONLNdq_æk(hædoes not mean that°dONLNdÑkZwî(tZthere isn’t a °dONLNdíkîw):Pbug.  This merely means that the program is lucky, and that it should be playing°dONLNd„wZÉ·(ÄZthe lottery, not running on a °dONLNdw·É)áBMacintosh.  If a program acts differently on different versions of°dONLNdDÑZêd(çZ8the Macintosh, you should think “could there be a nasty °dONLNd|Édèy(çdnil°dONLNdÑyêÓ) pointer problem here?”  °dONLNdòÑÓê)u     Use of a°dONLNd¢êZúo(öZnil°dONLNd•ëoù)R handle usually culminates in reading or writing to obscure places in memory.  As °dONLNd˜ëù(öan°dONLNd˙ùZ©Ñ(¶Zexample:
  596.     °dONLNdµ~¿+$VAR   myHandle:  TEHandle;°dONLNd …~‘Œ*myHandle := nil;
  597. °dONLNd1flZΑ(ËZPThat’s pretty straightforward, so what’s the problem?  If you do something like:
  598.     °dONLNdɘ~◊+$EmyHandle^^.viewRect := myRect;  { very bad idea with myHandle = nil }
  599. °dONLNd…
  600. ZD(Z2memory location zero will be used as a pointer to °dONLNd˚
  601. D)Í,give the address of a TextEdit record.  What°dONLNd(Z%â("Z@if that memory location points to something in the system heap? °dONLNdhâ%("â What if it points to the sound°dONLNdà%Z1(.Zbuffer? °dONLNdê%1)%V In cases like these, eight bytes of rectangle data will be written to wherever memory°dONLNdÁ1Z=¨(:Zlocation 0 points.°dONLNd¸JZVÖ*    Use of a °dONLNdIÖUö)+nil°dONLNdJöV⁄)C handle will never be useful.  This memory is reserved and used by °dONLNdKJ⁄V(S⁄
  602. the 68000 for°dONLNdYVZbÈ(_ZRvarious interrupt vectors and Valuable Stuff.  This Valuable Stuff is composed of °dONLNd´VÈb(_È things that°dONLNd∑bZn/(kZ,you definitely do not want to change.  When °dONLNd„b/n)’0changed, the 68000 finds out, and decides to get°dONLNdnZz(wZXback at your program in the most strange and wonderful ways.  These strange results can °dONLNdlnz(wrange°dONLNdrzZÜ(ÉZ#from a System Error all the way to °dONLNdïzÜ)¶<erasing hard disks and destroying files.  There really is no°dONLNd“ÜZí„(èZlimit to the havoc that can be °dONLNdÒÜ„í)âAwreaked.  This tends to keep the users on the edge of their seat,°dONLNd3íZû“(õZQbut this is not really the desired effect.  As noted above, it won’t necessarily °dONLNdÑí“û(õ“cause traumatic°dONLNdîûZ™‘(ßZMresults.  A program can be doing naughty things and not get caught.  This is °dONLNd·û‘™(ß‘still a bug that ¿X¿
  603. (’ZOV 4 - Compatibility: Why & How(’˙11)
  604.  of 21(ÎZOverviewˇfHRˇ ˇˇˇˇRH
  605. HR,Times
  606. .+6-Macintosh Technical Notes /4/˘
  607. °dONLNd<6H*1needs to be fixed, since it is nearly guaranteed °dONLNd1<H¯)‹2to give different results on different versions of°dONLNddH6Tà(Q6Cthe Macintosh.  Programs exhibiting schizophrenia have been proven °dONLNdßHàT¯(Qàto be less enjoyable to°dONLNdøT6`J(]6use.°dONLNdƒl6x’*How to avoid being a Niller°dONLNd‡Ñ6ê~*DWhenever a program uses pointers and handles, it should ensure that °dONLNd$Ñ~ê¯(ç~the pointer or handle will°dONLNd?ë6ùV(ö6not be ,
  608. Courier°dONLNdFêVúk) nil°dONLNdIëkù¯)P.  This could be termed defensive programming, since it assumes that everyone is°dONLNdöù6© (¶6.out to get the program (which is not far from °dONLNd»ù ©¯)÷/the truth on the Macintosh).  You should always°dONLNd¯™6∂ì(≥6check the result of °dONLNd ™ì∂„)]Croutines that claim to pass back a handle. If they pass you back a °dONLNdO©„µ¯(≥„nil°dONLNdS∂6¬8(ø63handle, you could get in trouble if you use them.  °dONLNdÜ∂8¬¯(ø8#Don’t trust the ROM.  The following°dONLNd™¬6ŒÃ(À6Kexample of a defensive use of a handle involves the Resource Manager.  The °dONLNdı¬ÃŒ¯(ÀÃResource°dONLNd˛Œ6⁄Ñ(◊6Manager passes °dONLNd
  609. ŒÑ⁄¯)NLback a handle to the resource data.  There are any number of places where it°dONLNdZ€6Á¡(‰6may be forced to pass back a °dONLNdw⁄¡Ê÷)ãnil°dONLNdz€÷Á@) handle.  For example:
  610.     °dONLNdíÛZ˛‹(˚ZVAR   myRezzie:  MyHandle;°dONLNdÆ    Z«*ImyRezzie := MyHandle(GetResource(myResType, myResNumber));    { could be °dONLNdz (z       missing…}°dONLNdZ(ã(%Z=IF myRezzie = nil  THEN  ErrorHandler('We almost got Nilled')°dONLNdQ'Z2*
  611. %ELSE  myRezzie^^.myRect := newRect;  °dONLNdw'22õ)ÿ { We know it is OK }
  612. °dONLNdç=6I(F6)As another example, think of how handles °dONLNd∂=I¯)Ÿ)can be purged from memory in tight memory°dONLNd‡I6Uº(R6Mconditions.  If a block is marked purgeable, the Memory Manager may throw it °dONLNd-IºU¯(Rº away at any°dONLNd9U6aΩ(^6Ptime.  This creates an empty handle.  The defensive programmer will always make °dONLNdâUΩa¯(^Ω
  613. sure that the°dONLNdóa6m◊(j6!handles being used are not empty.
  614.     °dONLNdºyZÑ‹+$VAR   myRezzie:  myHandle;°dONLNdÿçZòΩ*GmyRezzie := myHandle(GetResource(myResType, myResNumber));  { could be °dONLNd#󢢃+H
  615. :                                                missing… }°dONLNd_°Z¨ã(©Z=IF myRezzie = nil  THEN  ErrorHandler('We almost got Nilled')°dONLNdû´Z∂    *
  616. #ELSE  myRezzie^^.myRect := newRect;°dONLNd¬´∂r)¥{ We know it is OK }°dONLNdÿµZ¿(ΩZ%tempHandle := NewHandle (largeBlock);°dONLNd˛µ2¿π)ÿ{might dispose a purgeable °dONLNd!ø2 d*
  617.  
  618.  myRezzie}°dONLNd-…Z‘Ω(—ZGIF myRezzie^ = nil  THEN LoadResource(Handle(myRezzie)); {Re-load empty°dONLNdx”¢fi°+H
  619. 3                                            handle}°dONLNd≠›ZË◊(ÂZIF ResError = noErr  THEN°dONLNd…ÁZÚˇ*
  620. !    myRezzie^^.StatusField := OK;°dONLNdÎÁÚ«)¥%{ guaranteed not empty, and actually °dONLNdÒ¢¸ø(˘¢9                        gets read back in, if necessary }
  621. °dONLNdM6†(6Be especially careful °dONLNdc†¢)j0of places where memory is being allocated.  The °dONLNd좷(¢    NewHandle°dONLNdú·¯)? and°dONLNd°6 `(6NewPtr°dONLNdß`!Ø)* calls will return °dONLNd∫Ø!∏)Oa °dONLNdº∏ Õ)    nil°dONLNdøÕ!¯)= handle or pointer if there is not enough memory.  If you use°dONLNd˝!6-ú(*6Nthat handle or pointer without checking, you will be guilty of being a Niller.°dONLNd    L96E¶*How to find Nillers°dONLNd    `R6^—*!The best way to find these nasty °dONLNd    ÅQ—]Ê)õnil°dONLNd    ÑRÊ^G) pointer problems is °dONLNd    ôRG^¯)a$to set memory location zero to be an°dONLNd    æ_6kK(h6odd°dONLNd    ¡_Kk˝)# number (a good choice is 'NIL!' = °dONLNd    ‰^˝j<)≤    $4E494C21°dONLNd    Ì_<kΔ)?, which is numerically odd, °dONLNd
  622.     _Δk¯)ä
  623. as well as°dONLNd
  624. k6wΔ(t6Tpersonality-wise).  Please see OV 15 - Debugging Tips for details on how to do this.°dONLNd
  625. kÉ6è¨*If you use TMON, you °dONLNd
  626. Äɨè¯)vDcan use the extended user area with Discipline.  Discipline will set°dONLNd
  627. ≈è6õ6(ò66memory location 0 to 'NIL!' to help catch those nasty °dONLNd
  628. ˚è6õ¯(ò6&pointer problems.  If you use Macsbug,°dONLNd "ú6®a(•6
  629. just type °dONLNd ,õaß})+SM 0°dONLNd 0ú}®Ä) °dONLNd 1õÄߣ)'NIL!°dONLNd 6ú£®ı)# and go.  Realize °dONLNd Húı®¯)R5of course, that if a program has made a transgression°dONLNd ~©6µù(≤6and is actually using °dONLNd î®ù¥≤)gnil°dONLNd ó©≤µ‰)  pointers, °dONLNd ¢©‰µ¯)23this may make the program crash with an ID=2 system ¿4¿˘
  630. (’612)
  631.  of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇ–HRˇ ˇˇˇˇRH
  632. HR,Times
  633. .+Z-Developer Support Center(-Ï
  634. March 1988 /X/
  635. °dONLNd<ZHû(EZerror.  This is °dONLNd<ûH)DJgood!  This means that you have found a bug that may have been causing you°dONLNd[HZTz(QZ6untold grief.  Once you know where a program crashes, °dONLNdëHzT(Qz it is usually very easy to use a°dONLNd≤TZ`Δ(]ZMdebugger to find where the error is in the source code.  When the program is °dONLNdˇTΔ`(]Δcompiled, turn on°dONLNd`Zl(iZ$the debugging labels (usually a $D+ °dONLNd5`l)≠7option).  Set memory location 0 to be 'NIL!'.  When the°dONLNdmlZx¯(uZWprogram crashes, look at where the program is executing and see what routine it was in °dONLNdƒl¯x(u¯(from a°dONLNdÃxZÑ˛(ÅZXdisassembly).  Go back to that routine in the source code and remove the offending code °dONLNd$x˛Ñ(Şwith a°dONLNd+ÑZêH(çZ1grim smile on your face.  Another scurvy bug has °dONLNd\ÑHê)Ó+been vanquished.  The intoxicating smell of°dONLNdàêZúÚ(ôZvictory wafts around your head.°dONLNd®®Z¥L*2Another way to find problems is to use a debugger °dONLNd⁄®L¥)Ú+to do a checksum on the first four bytes in°dONLNd¥Z¿¯(ΩZ!memory (from 0 to 3 inclusive).  °dONLNd'¥¯¿)û=If the program ever traps into the debugger claiming that the°dONLNde¿ZÃm(…Z6memory changed, see which part of the program altered °dONLNdõ¿mÃ(…m!memory location 0.  Any code that°dONLNdΩÃZÿó(’ZEwrites to memory location zero is guilty of high treason against the °dONLNdÃóÿ(’óstate and must be removed.°dONLNdÿZ‰1(·Z+Remember to say, “bugs are not my friends.”°dONLNdJ¸Z*$Creating or Using Fake Handles°dONLNdiZ ñ*BA fake handle is one that was not manufactured by the system, but °dONLNd´ñ (ñwas created by the program°dONLNdΔ Z,()Z(itself.  An example of a fake handle is:,
  636. Courier
  637.     °dONLNd8~Có+$CONST°dONLNdˆ8¢Cfi)$ aMem = $100;°dONLNdB~Mç(J~VAR°dONLNdB¢M˜)$myHandle: Handle;°dONLNdL¢WÌ*
  638. myPointer: Ptr;°dONLNd-b~mˆ(j~myPointer := Ptr (aMem);°dONLNdFbm§)ê{ the address of some memory }°dONLNdfl~wÒ(t~myHandle := @myPointer;°dONLNd~lw˛)ê0{the address of the pointer variable. Very bad.}
  639. °dONLNdØÉZèq(åZThe °dONLNd≥Éqè›)Cnormal way to create and use handles is to call the Memory Manager °dONLNdˆÇ›é(å›    NewHandle°dONLNdèZõÖ(òZ    function.°dONLNd ßZ≥•* Why it’s Bad°dONLNdøZÀ    *%A handle that is manufactured by the °dONLNd=ø    À)Ø:program is not a legitimate handle as far as the operating°dONLNdxÀZ◊√(‘Zsystem is concerned. °dONLNdçÀ√◊)iD Passing a fake handle to routines that use handles is a good way to°dONLNd“◊Z„F(‡Z-discover the meaning of “Death by ROM.”  For °dONLNdˇ◊F„)Ï)example, think how confused the operating°dONLNd)‰ZM(ÌZ3system would get if the fake handle were passed to °dONLNd\„MÔ°)Û DisposHandle°dONLNdh‰°«)T.  What °dONLNdp‰«)&would it dispose?°dONLNdÉZ¸[(˘Z5It never allocated the memory, so how can it release °dONLNd∏[¸(˘[&it?  Programs that manufacture handles°dONLNdfl¸Zr(Z=may find that the operating system is no longer their friend.°dONLNdZ Ñ*=When handles are passed to various ROM routines, there is no °dONLNdZÑ (Ñ!telling what sorts of things will°dONLNd| Z,()ZZbe done to the handle.  There are any number of normal handle manipulation calls that the °dONLNd÷ ,()ROM°dONLNd⁄-Z9≤(6Zmay use, such as °dONLNdÎ,≤8
  640. )X
  641. SetHandleSize°dONLNd¯-
  642. 9)[, °dONLNd˙,88)HLock°dONLNdˇ-89A)#, °dONLNd    ,A8y)    HNoPurge°dONLNd        -y9Ç)8, °dONLNd     ,Ç8≥)    MoveHHi°dONLNd    -≥9)1 and so on.  Since a°dONLNd    '9ZE¡(BZIprogram cannot guarantee that the ROM will not be doing things like this °dONLNd    p9¡E(B¡to handles that the°dONLNd    ÑEZQ»(NZPprogram passes in, it is wise to make sure that a real handle is being used, so °dONLNd    ‘E»Q(N»that all these type°dONLNd    ËRZ^ù([Zof operations °dONLNd    ˆRù^„)C@will work as the ROM expects.  For fake handles, the calls like °dONLNd
  643. 6Q„]([„HLock°dONLNd
  644. ;R^)# and°dONLNd
  645. @^Zjµ(hZ
  646. SetHandleSize°dONLNd
  647. M_µk∏)[ °dONLNd
  648. N_∏k)Ihave no bearing.  Fake handles are very easy to create, and they are very°dONLNd
  649. òkZw¯(tZRbad for the health of otherwise upstanding programs.  Whenever you need a handle, °dONLNd
  650. Ík¯w(t¯get one°dONLNd
  651. ÚwZÉ›(ÄZfrom the Memory Manager.°dONLNd
  652. èZõ*+As a particularly bad use of a fake handle:
  653.     °dONLNd :ß~≤ˆ+$VAR   myHandle:  Handle; ¿X¿
  654. (’ZOV 4 - Compatibility: Why & How(’˙13)
  655.  of 21(ÎZOverviewˇ∂HRˇ ˇˇˇˇRH
  656. HR,Times
  657. .+6-Macintosh Technical Notes /4/˘,
  658. Courier
  659.     °dONLNd<ZG◊+$      myStuff:  myRecord;°dONLNdPZ[«*ImyHandle := NewHandle (SIZEOF(myStuff));   { create a new normal handle }°dONLNdfZZeÆ*
  660. DmyHandle^ := @myStuff;  {YOW!  Intended to make myHandle a handle to°dONLNd≠d~oë+$
  661. 7            the myStuff record.  What it really does is°dONLNdÁn~y™*
  662. <            blow up a Master Pointer block, Heap corruption,°dONLNd&x~ÉÇ*
  663. 4            and death by Bad Heap.  Never do this. }
  664. °dONLNd[é6ö‚(ó6_This can be a little confusing, since it is fine to use your own pointers, but very bad to use °dONLNd∫é‚ö¯(ó‚your°dONLNdøö6¶    (£6-own handles.  The difference is that handles °dONLNdÏö    ¶¯)”.can move in memory, and pointers cannot, hence°dONLNd¶6≤7(Ø64the pointers are not dangerous.  This does not mean °dONLNdO¶7≤¯(Ø7&you should use pointers for everything°dONLNdv≤6æt(ª6Dsince that causes other problems.  It merely means that you have to °dONLNd∫≤tæ¯(ªtbe careful how you use the°dONLNd’æ6 ^(«6handles.°dONLNdfl÷6‚^*The use °dONLNdÁ÷^‚¯)(Sof fake handles usually causes system errors, but can be somewhat mysterious in its°dONLNd;‚6Óø(Î6Ueffects.  Fake handles can be particularly hard to track down since they often cause °dONLNdê‚øÓ¯(Îø damage that°dONLNdúÓ6˙.(˜63is not uncovered for many minutes of use.  Any use °dONLNdœÓ.˙¯)¯*of fake handles that causes the heap to be°dONLNd˙˙6Æ(6altered will usually crash °dONLNd˙Ư)xCthe system.  Heap corruption is a common failure mode.  In clinical°dONLNdY6ı(6Zstudies, 9 out of 10 programmers recommend uncorrupted heaps to their users who use heaps.°dONLNd¥6*œ*How to avoid being a fakir°dONLNdœ66Bû*LThe correct way to make a handle to some data is to make a copy of the data:
  665.     °dONLNdNZY“+$VAR   myHandle:  Handle;°dONLNd7XZc◊*
  666.       myStuff:  myRecord;°dONLNdRnZyÅ*;errCode := PtrToHand (@myStuff, myHandle, SIZEOF(myStuff));°dONLNdèxZÉ|*
  667. :IF  errCode <> noErr  THEN ErrorHandler ('Out of memory');
  668. °dONLNd é6ö\(ó6:Always, always, let the Memory Manager perform operations °dONLNdé\ö¯(ó\with handles.  Never write code°dONLNd$ö6¶(£61that assigns something to a master pointer, like:
  669.     °dONLNdW≤ZΩÕ+$VAR   myDeath:  Handle;°dONLNdpºZ«r*
  670. 8myDeath^ := stuff;  { Don’t change the Master pointer. }
  671. °dONLNd©“6fiÄ(€6JIf there is code like this, it usually means the heap is being corrupted, °dONLNdÛ“Äfi¯(€Äor a fake handle is being°dONLNd
  672. fi6Í`(Á6Aused.  It is, however, OK to pass around the handle itself, like:
  673.     °dONLNdPˆZÃ+$JmyCopyHandle := myHandle;   { perfectly OK, nobody will yell about this. }
  674. °dONLNdõ
  675. 6Á(6%This is far different than using the °dONLNd¿ ÁÓ)±^°dONLNd¡
  676. Ó”)/ operator to accidentally modify things in the °dONLNd
  677. ”¯)Âsystem.°dONLNd˘6%¿("6QWhenever it is necessary to write code to use handles, be careful.  Watch things °dONLNdJ¿%¯("¿ carefully as°dONLNdW%61B(.6<they are being written.  It is much easier to be careful on °dONLNdì%B1¯(.B(the way in than it is to try to find out°dONLNdº26>¿(;6why something is crashing. °dONLNd◊2¿>8)ä Be very careful of the °dONLNdÔ18=?)x@°dONLNd2?>¯)% operator.  This operator can unleash°dONLNd    >6J=(G67untold problems upon unsuspecting programs.  If at all °dONLNd    M>=J¯(G=*possible, try to avoid using it, but if it°dONLNd    xJ6V°(S6Pis necessary, be absolutely sure you know what it is doing.  It is particularly °dONLNd    »J°V¯(S°dangerous since it°dONLNd    €V6bü(_6Oturns off the normal type checking that can help you find errors (in Pascal).  °dONLNd
  678. *Vüb¯(_üIn short, don’t get°dONLNd
  679. >b6n≤(k6Ocrazy with pointer and handle manipulations, and they won’t get crazy with you. ¿4¿˘
  680. *j14)
  681.  of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇÃHRˇ ˇˇˇˇRH
  682. HR,Times
  683. .+Z-Developer Support Center(-Ï
  684. March 1988 /X/
  685. °dONLNd<ZHƒ(EZHow to find fakirs°dONLNdTZ`v*<Problems of this form are particularly insidious because it °dONLNdOTv`(]v"can be very difficult to find them°dONLNdr`ZlR(iZ6after they have been created.  They tend to not crash °dONLNd®`Rl)¯)immediately, but rather to crash sometime°dONLNd“lZx(uZYlong after the real damage has been done.  The best way to find these problems is to run °dONLNd+lx(uthe°dONLNd/xZÑÈ(ÅZQprogram with Discipline.  (Discipline is a programmer’s tool that will check all °dONLNdÄxÈÑ(ÅÈ
  686. parameters°dONLNdãÑZê;(çZ1passed to the ROM to see if they are legitimate. °dONLNdºÑ;ê)·/ Discipline can be found as a stand-alone tool,°dONLNdÏêZú(ôZ%but the most up-to-date version will °dONLNdêú)∑/be found in the Extended User Area for the TMON°dONLNdAúZ®?(•Z/debugger.  The User Area is public domain, but °dONLNdpú?®)Â)TMON itself is not.  TMON has a number of°dONLNdö®Z¥î(±ZAother useful features, and is well worth the price.)  Discipline °dONLNd€®î¥(±îwill check handles that are°dONLNd˜¥Z¿÷(ΩZTpassed to the ROM to see if they are real handles or not, and if not, will stop the °dONLNdK¥÷¿(Ω÷program at the°dONLNdZ¿ZÃ˚(…Z#offending call.  This can lead you °dONLNd}¿˚Ã)°<back to the source at a point that may be close to where the°dONLNd∫ÃZÿó(’ZBbad handle was created.  If a program passes the Discipline test, °dONLNd¸Ãóÿ(’óit will be a healthy, robust°dONLNdÿZ‰à(·Zprogram °dONLNd!ÿà‰).Lwith drastically improved odds for compatibility.  Programs that do not pass°dONLNdn‰Z‹(ÌZDiscipline can sleep poorly °dONLNd䉋)ÇBat night, knowing that they have broken at least one or two of the°dONLNdÕZ¸(˘Z“rules.”°dONLNdŸZ5*-A way to find programs that are damaging the °dONLNd5)€+heap is to use a debugger (TMON or Macsbug)°dONLNd2Z  (Z^and turn on the Heap Check operation.  This will check the heap for errors at each trap call, °dONLNdê  ( and°dONLNdî Z,ä()ZBif the heap is corrupted will break into the debugger.  Hopefully °dONLNd÷ ä,()äthis will be close to where the°dONLNdˆ,Z8}(5Z>code is that caused the damage.  Unfortunately, it may not be °dONLNd4,}8(5}!close enough; this will force you°dONLNdV8ZDª(AZto look further back.°dONLNdnQZ]N*5Looking in the source code, look for all uses of the ,
  687. Courier°dONLNd£PN\U)Ù@°dONLNd§QU]X) °dONLNd•QX])(operator, and examine the code carefully°dONLNdŒ]Ziv(fZAto see if it is breaking the rules.  If it is, change it to step °dONLNd]vi(fv"in line with the rest of the happy°dONLNd2iZu((rZ+programs here in happy valley.  Also, look °dONLNd]i(u)Œ3for any code that changes a master pointer like the°dONLNdëuZÅÿ(ZmyHandle^ := stuff°dONLNd£vÿǢ)~.  Any °dONLNd™v˘Ç)!:code of this form is highly suspect, and probably a member°dONLNdÂÇZé(ãZ&of the Anti-Productivity League.  The °dONLNd Çé)¥5APL has been accused of preventing software sales and°dONLNdAéZöù(óZthe rise of the °dONLNdQéùö)CPYen.  These problems can be quite difficult to find at times, but don’t give up.°dONLNd£öZ¶Ë(£ZWThese fake handles are high on the list of guilty parties, and should never be trusted.°dONLNd˝≤Zæ*!Writing code that modifies itself°dONLNd      Z÷    *%Self-modifying code is software that °dONLNd    E     ÷)Ø;changes itself.  Code that alters itself runs into two main°dONLNd    Å÷Z‚Ÿ(flZSgroupings:  code that modifies the code itself and code that changes the block the °dONLNd    ‘÷Ÿ‚(flŸcode is stored°dONLNd    „‚ZÓ∏(ÎZDin.  Copy protection code often modifies the code itself, to change °dONLNd
  688. '‚∏Ó(Î∏the way it operates°dONLNd
  689. ;ÓZ˙œ(˜Z(concealing the meaning °dONLNd
  690. SÓœ˙)uEof what the code does).  Changing the code itself is very tricky, and°dONLNd
  691. ô˙Z¢(ZDalso prone to having problems, particularly when the microprocessor °dONLNd
  692. ›˙¢(¢itself changes.  There are°dONLNd
  693. ¯Z≥(ZIthird-party upgrades available that add a 68020 to a Macintosh.  Because °dONLNd A≥(≥of the 68020’s cache,°dONLNd WZ(ZPprograms that modify themselves stand a good chance of having problems when run °dONLNd ß(on a°dONLNd ¨Z*('Z68020. °dONLNd ≥*)%T This is a compatibility point that should not be missed (nudge, nudge, wink, wink).°dONLNd     *Z6v(3ZCode °dONLNd *v6)Wthat changes other code (or itself) is prone to be incompatible when the microprocessor°dONLNd f6ZBÑ(?Zchanges.°dONLNd qNZZ*&The second group is code that changes °dONLNd óNZ)π8the block that the code is stored in.  Keeping variables°dONLNd –ZZfc(cZ2in the CODE segment itself is an example of this. °dONLNd
  694. Zcf(cc! This is uncommon with high-level°dONLNd
  695. $fZr6(oZ,languages, but it is easy to do in assembly °dONLNd
  696. Pf6r)‹-language (using the DC directive).  Variables°dONLNd
  697. ~rZ~o({Z=defined in the code itself should be read-only (constants).  °dONLNd
  698. ªro~({o$Code that modifies itself has signed°dONLNd
  699. ‡~Zäî(áZIa tacit agreement that says “I’m being tricky, if I die, I’ll revise it.” ¿X¿
  700. *NOV 4 - Compatibility: Why & How(’˙15)
  701.  of 21(ÎZOverviewˇËHRˇ ˇˇˇˇRH
  702. HR,Times
  703. .+6-Macintosh Technical Notes /4/˘
  704. °dONLNd<6HÅ* Why it’s Bad°dONLNd
  705. T6`**There are now three different versions of °dONLNd7T`¯)“-the microprocessor, the 68000, 68010, and the°dONLNde`6l„(i6V68020.  They are intended to be compatible with each other, but may not be compatible °dONLNdª`„l¯(i„with°dONLNd¿l6xQ(u6code °dONLNd≈lQx¯)Rthat modifies itself.  As the Macintosh evolves, the system may have compatibility°dONLNdx6Ñ@(Å67problems with programs that try to “push the envelope.”°dONLNdPê6ú‡*How to avoid being an abuser°dONLNdm®6¥q*BWell, the obvious answer is to avoid writing self-modifying code. °dONLNdØ®q¥¯(±q If you feel obliged to write°dONLNdÕ¥6¿π(Ω6Pself-modifying code, then you are taking an oath to not complain when you break °dONLNd¥π¿¯(Ωπin the future.°dONLNd-¿6û(…6TBut don’t worry about accidentally taking the oath: you won’t do it without knowing °dONLNdÅ¿»Ã¯(…» it.  If you°dONLNdçÃ6ÿœ(’6 choose to abuse, you also agree °dONLNd≠Üÿ¯)ô=to personal visits from the Apple thought police, who will be°dONLNdÎÿ6‰ø(·6hired as soon as we find out.°dONLNd    6¸™*How to find abusers°dONLNd6ò*Run the program on °dONLNd0ò¯)bLa 68020 system.  If it fails, it could be related to this problem, but since°dONLNd}6 ñ(6there are other bugs °dONLNdíñ ¯)`Kthat might cause failures, it is not guaranteed to be a self-modifying code°dONLNdfi 6,È()6\problem.  Self-modifying code is often used in copy protection, which brings us to the next °dONLNd: È,¯()Èbig°dONLNd>,68Q(56topic.°dONLNdED6P!*)Code designed strictly as copy protection°dONLNdo\6hX*?Copy protection is used to make it difficult to make copies of °dONLNdÆ\Xh¯(eX a program.  The basic premise is°dONLNdœh6tg(q6 to make it °dONLNd⁄hgt¯)1Rimpossible to copy a program with the Finder.  This will not be a discussion as to°dONLNd-t6Ä(}60the pros and cons of copy protection.  Everyone °dONLNd]tį)Ê.has an opinion.  This will be a description of°dONLNdåÄ6åfi(â6(reality, as it relates to compatibility.°dONLNd∑ò6§Å* Why it’s Bad°dONLNdƒ∞6º-*2System changes will never be made merely to cause °dONLNdˆ∞-º¯)˜*copy protection schemes to fail, but given°dONLNd!º6»$(≈6,the choice between improving the system and °dONLNdMº$»¯)Ó&making a copy protection scheme remain°dONLNdt»6‘R(—69compatible, the system improvement will always be chosen.°dONLNdƇHÏL+•°dONLNd∞‡UÏ()
  706. -Copy protection is number one on the list of °dONLNd›‡(Ï‘)”#why programs fail the compatibility°dONLNdÏU¯i(ıUtest.°dONLNd¯HL(H•°dONLNd    ¯U•)
  707. ICopy protection by its very nature tends to do the most “illegal” things.°dONLNdUHL(
  708. H•°dONLNdWUw)
  709. <Programs that are copy protected are assumed to have signed °dONLNdìw‘(
  710. wa tacit agreement to°dONLNd®U,(U+revise the program when the system changes.°dONLNd÷(64(160Copy protection itself is not necessarily bad.  °dONLNd(4¯)Ë'What is bad is when programs that would°dONLNd.46@”(=6!otherwise be fully compatible do °dONLNdO4”@¯)ù<not work due only to the copy protection.  This is very sad,°dONLNdå@6Lc(I6Bsince it requires extra work, revisions to the software, and time °dONLNdŒ@cL¯(Ic lost while the revision is being°dONLNdÔL6XÆ(U6produced.  The users are °dONLNd    LÆX¯)xAnot generally humored when they can no longer use their programs.°dONLNd    KX6dE(a69Copy protection schemes that fail generally cause system °dONLNd    ÑXEd¯(aE$errors when they are run.  They also°dONLNd    ©d6p‡(m6#can refuse to run when they should.°dONLNd    œ|6à˚*"How to avoid being a protectionist°dONLNd    Úî6†É*GThe simple answer is to do without copy protection altogether.  If you °dONLNd
  711. 9îɆ¯(ùÉthink of compatibility as°dONLNd
  712. S†6¨Ò(©6)a probability game, if you leave out the °dONLNd
  713. |†Ò¨¯)ª4copy protection, your odds of winning skyrocket.  As ¿4¿˘
  714. (’616)
  715.  of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇHRˇ ˇˇˇˇRH
  716. HR,Times
  717. .+Z-Developer Support Center(-Ï
  718. March 1988 /X/
  719. °dONLNd<ZHˆ(EZ noted above, copy protection is °dONLNd <ˆH)ú:the single biggest reason why programs fail on the various°dONLNd[HZTí(QZ versions of °dONLNdgHíT)8Qthe Macintosh.  For those who are required to use copy protection, try to rely on°dONLNdπTZ`è(]Z<schemes that do not require specific hardware and make sure °dONLNdıTè`(]èthat the scheme used is not°dONLNd`Zlµ(iZperforming illegal °dONLNd$`µl)[Coperations.  If a program runs, an experienced Macintosh programmer°dONLNdhlZx-(uZ*armed with a debugger can probably make a °dONLNdíl-x)”1copy of it, (no matter how sophisticated the copy°dONLNdƒxZÑ«(ÅZprotection scheme) so °dONLNd⁄x«Ñ)mDa moderate scheme that does not break the rules is probably a better°dONLNdÑZêØ(çZIcompatibility bet.  The trickier and more devious the scheme, the higher °dONLNdhÑØê(çØthe chance of breaking°dONLNdêZúæ(ôZa rule.  Tread lightly.°dONLNdó®Z¥*How to find protectionists°dONLNd≤¿ZÃò*GThe easiest way to see if a scheme is being overly tricky is to run it °dONLNd˘¿òÃ(…òon a Macintosh XL.  Since°dONLNdÃZÿ(’Z&the floppy disk hardware is different °dONLNd9Ãÿ)ª2this will usually demonstrate an unwanted hardware°dONLNdlÿZ‰/(·Z+dependency.  Be wary of schemes that don’t °dONLNdóÿ/‰)’2allow installation on a hard disk.  If the program°dONLNd ‰Z–(ÌZcannot be installed on a °dONLNd„‰–)vBhard disk, it may be relying upon things that are prone to change.°dONLNd'Z¸º(˘ZDon’t use schemes °dONLNd9º¸)bDthat access the hardware directly.  All Macintosh software should go°dONLNd~¸Zì(Z through the °dONLNdä¸ì)9Ovarious managers in the ROM to maintain compatibility.  Any code that sidesteps°dONLNd⁄Z√(ZIthe ROM will be viewed as having said “It’s OK to make me revise myself.”°dONLNd& Z,C*)Check errors returned as function results°dONLNdP8ZD*VAll of the Operating System functions, as well as some of the Toolbox functions, will °dONLNd¶8D(Areturn°dONLNd≠DZPÕ(MZSresult codes as the value of the function.  Don’t ignore these result codes.  If a °dONLNdDÕP(MÕprogram ignores°dONLNdPZ\æ(YZthe result codes, it is °dONLNd(Pæ\)dEpossible to have any number of bad things happen to the program.  The°dONLNdn\Zhõ(eZresult code is °dONLNd}\õh)AOthere to tell the program that something went wrong; if the program ignores the°dONLNdÕhZt›(qZOfact that something is wrong, that program will probably be killed by whatever °dONLNdh›t(q› went wrong.°dONLNd)tZÄ(}Z?(Bugs do not like to be ignored.)  If a program checks errors, °dONLNdhtÄ(}an anomaly can be nipped in the°dONLNdàÄZå3(âZ-bud, before something really bizarre happens.°dONLNd∂òZ§•* Why it’s Bad°dONLNd√∞Zº*'A program that ignores result codes is °dONLNdÍ∞º)Ω4skipping valuable information.  This information can°dONLNdºZ»ö(≈ZCoften prevent a program from crashing and keep it from losing data.°dONLNde‘Z‡˙*How to avoid becoming a skipper°dONLNdÖÏZ¯…*Always write code that °dONLNdúÏ…¯)oFis defensive.  Assume that everyone and everything is out to kill you.°dONLNd‰¯Z:(Z/Trust no one.  An example of error checking is:,
  720. Courier
  721.     °dONLNd    ~_+$-myRezzie := GetResource (myResType, myResId);°dONLNd    D~%“*
  722. DIF  myRezzie = nil  THEN  ErrorHandler ('Who stole my resource...');
  723. °dONLNd    â0Z<≠(9ZAnother example:
  724.     °dONLNd    õH~Sñ+$8fsErrCode := FSOpen ('MyFile', myVRefNum, myFileRefNum);°dONLNd    ’R~]Õ*
  725. CIF fsErrCode <> noErr  THEN ErrorHandler (fsErrCode, 'File error');
  726. °dONLNd
  727. hZtó(qZ And another:
  728.     °dONLNd
  729. 'Ä~ãd+$.myTPPrPort := PrOpenDoc (myTHPrint, nil, nil);°dONLNd
  730. Wä~ï◊*
  731. EIF  PRError <> noErr  THEN  ErrorHandler (PRError, 'Printing error'); ¿X¿
  732. (’ZOV 4 - Compatibility: Why & How(’˙17)
  733.  of 21(ÎZOverviewˇºHRˇ ˇˇˇˇRH
  734. HR,Times
  735. .+6-Macintosh Technical Notes /4/˘
  736. °dONLNd<6H*-Any use of Operating System functions should °dONLNd-<H¯)‡,presume that something nasty can happen, and°dONLNdZH6T?(Q6;have code to handle the nasty situations.  Printing calls, °dONLNdïH?T¯(Q?$File Manager calls, Resource Manager°dONLNd∫T6`Ì(]6[calls, and Memory Manager calls are all examples of Operating System functions that should °dONLNdTÌ`¯(]Ìbe°dONLNd`6l`(i6watched °dONLNd ``l¯)*Pfor returning errors.  Always, always check the result codes from Memory Manager°dONLNdql6x¥(u6Icalls.  Big memory machines are pretty common now, and it is easy to get °dONLNd∫l¥x¯(u¥cavalier about°dONLNd…x6Ñÿ(Å6!memory, but realize that someone °dONLNdÍxÿѯ)¢9will always want to run the program under Switcher, or on°dONLNd$Ñ6êö(ç6Msmaller Macintoshes.  It never hurts to check, and always hurts to ignore it.°dONLNdtú6®Ø*How to find skippers°dONLNdâ¥6¿Ú*)This is easy: just do weird things while °dONLNd≤¥Ú¿¯)º5the program is running.  Put in locked or unformatted°dONLNdË¿6Ã(…6)disks while the program is running.  Use °dONLNd¿Ã¯)Õ-unconventional command sequences.  Run out of°dONLNd?Ã6ÿå(’6disk space.  Run °dONLNdPÃåÿ¯)VDon 128K Macintoshes to see how the program deals with running out of°dONLNdïÿ6‰›(·6Smemory.  Run under Switcher for the same reason.  (Programs that die while running °dONLNdËÿ›‰¯(·›under°dONLNdÓ‰6I(Ì6<Switcher are often not Switcher’s fault, and are in fact due°dONLNd+¸69* °dONLNd,¸9¿)Qto faulty memory management.)  Print with no printer connected to the Macintosh. °dONLNd}¸¿¯(¿   Pop disks°dONLNdâ6H(6out °dONLNdçH¯)Vof the drives with the Command-Shift sequence, and see if the program can deal with no°dONLNd‰6 Ô(6Vdisk.  When a disk-switch dialog comes up, press Command-period to pass back an error °dONLNd:Ô ¯(Ôto°dONLNd= 6,
  737. ()6*the requesting program (128K ROMs only).  °dONLNdg 
  738. ,¯)◊.Torturing otherwise well- behaved programs can°dONLNdñ,68X(56>be quite enjoyable, and a number of users enjoy torturing the °dONLNd‘,X8¯(5Xprogram as much as the program°dONLNdÛ86D (A6 enjoys torturing them.  For the °dONLNd8 D¯)î?truly malicious, run the debugger and alter error codes as they°dONLNdSD6P(M6,come back from various routines.  Sure it’s °dONLNdDP¯)–1a dirty low-down rotten thing to do to a program,°dONLNd±P6\V(Y6;but we want to see how far we can push the program.  (This °dONLNdÏPV\¯(YV is also a good way to check your°dONLNd
  739. \6hÆ(e6error handling.)  It’s one °dONLNd(\Æh¯)xFthing to be an optimist, but it’s quite another to assume that nothing°dONLNdoh6t(q6)will go wrong while a program is running.°dONLNdöÄ6å÷*Accessing hardware directly°dONLNd∂ò6§˙*)Sometimes it is necessary to go directly °dONLNdflò˙§¯)ƒ2to the Macintosh hardware to accomplish a specific°dONLNd§6∞m(≠6?task for which there is no ROM support.  Early hard disks that °dONLNdQ§m∞¯(≠mused the serial ports had no°dONLNdn∞6ºS(π6ROM °dONLNdr∞Sº¯)Ssupport.  Those disks needed to use the SCC chip (the 8530 communication chip) in a°dONLNdΔº6»ÿ(≈6Rhigh-speed clocked fashion.  Although it is a valid function, it is not something °dONLNd    ºÿ»¯(≈ÿthat is°dONLNd     »6‘v(—6
  740. supported in °dONLNd    -»v‘¯)@Kthe ROM.  It was therefore necessary to go play with the SCC chip directly,°dONLNd    y‘6‡3(›66setting and testing various hardware registers in the °dONLNd    Ø‘3‡¯)˝(chip itself.  Another example of a valid°dONLNd    ÿ‡6Ïi(È6=function that has no ROM support is the use of the alternate °dONLNd
  741. ‡iϯ(Èivideo page for page-flipping°dONLNd
  742. 2Ï6¯ß(ı6Qanimation.  Since there is no ROM call to flip pages, it is necessary to go play °dONLNd
  743. ÉÏ߯¯(ıßwith the right bit°dONLNd
  744. ñ¯6ä(6Gin the VIA chip (6522 Versatile Interface Adapter).  Going directly to °dONLNd
  745. ›¯ä¯(äthe hardware does not°dONLNd
  746. Û6„(
  747. 6\automatically throw a program into the incompatible group, but it certainly lowers its odds.°dONLNd P6(* Why it’s bad°dONLNd ]46@z*@Going directly to the hardware poses any number of problems for °dONLNd ù4z@¯(=zenlightened programs that°dONLNd ∑@6LÅ(I6Dare trying to maintain compatibility across the various versions of °dONLNd ˚@ÅL¯(IÅthe Macintosh.  On the°dONLNd L6Xé(U6Macintosh XL for °dONLNd #LéX¯)XKexample, a lot of the hardware is found in different locations, and in some°dONLNd oX6d∞(a6Ocases the hardware doesn’t exist.  On the XL there is no sound chip.  Programs °dONLNd æX∞d¯(a∞that go directly°dONLNd œd6p·(m6Yto the sound hardware will find they don’t work correctly on an XL.  If the same program °dONLNd
  748. (d·p¯(m·were°dONLNd
  749. -p6|⁄(y6Rto go through the Sound Manager, it would work fine, although the sound would not °dONLNd
  750. p⁄|¯(y⁄be the°dONLNd
  751. Ü|6à«(Ö6Psame as expected.  Since the Macintosh is heavily oriented to the software side °dONLNd
  752. ÷|«à¯(Ö«
  753. of things,°dONLNd
  754. ·à6îæ(ë6expecting various hardware °dONLNd
  755. ¸àæî¯)à=to always be available is not a safe bet.  Choosy programmers°dONLNd:î6†¸(ù6(choose to leave the hardware to the ROM. ¿4¿˘
  756. *818)
  757.  of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇFHRˇ ˇˇˇˇRH
  758. HR,Times
  759. .+Z-Developer Support Center(-Ï
  760. March 1988 /X/
  761. °dONLNd<ZH(EZ!How to avoid having a hard attack°dONLNd"TZ`z*Don’t °dONLNd(Tz`) Pread or write the hardware.  Exhaust every possible conventional approach before°dONLNdy`Zl(iZ+deciding to really get down and dirty.  If °dONLNd§`l)¬3there is a Manager in the ROM for the operation you°dONLNdÿlZx›(uZwish to perform, it is far °dONLNdÛl›x)É>better to use the Manager than to go directly to the hardware.°dONLNd3xZÑZ(ÅZ4Compatibility at the hardware level can very rarely °dONLNdgxZÑ(ÅZ'be maintained, but compatibility at the°dONLNdèÑZê°(çZManager level °dONLNdùѰê)GMis a prime consideration.  If a program is down to the last ditch effort, and°dONLNdÎêZú—(ôZcannot get the support °dONLNdê—ú)w<from the ROM that is desired, then access the hardware in an°dONLNd?úZ®I(•Z3enlightened approach.  The really bad way to do it:,
  762. Courier
  763.     °dONLNdt¥~øπ+$?VIA := Pointer ($EFE1FE);  { sure it’s the base address today…}°dONLNd∂梅¶+$
  764. 4                { This is bad.  Hard-coded number. }
  765. °dONLNdΑZ‡°(›ZEThe with-it, inspired programmer of the eighties does something like:
  766.     °dONLNd2Ï~˜
  767. +$TYPE LongPointer = ^LongInt;°dONLNdP~
  768. Ï*VAR  VIA: LongPointer;°dONLNdh ~Ï*
  769.      VIABase: LongInt;°dONLNdÄ ~+√*AVIA := Pointer ($1D4);  { the address of the low-memory global. }°dONLNd√*~5π*
  770. ?VIABase := VIA^;        { get the low-memory variable’s value }°dONLNd4¢?Œ+$
  771. <                 { Now VIABase has the address of the chip }
  772. °dONLNdBJZVä(SZ
  773. The point °dONLNdLJäV)0There is that the best way to get the address of a hardware chip is to ask the system°dONLNd°VZb˚(_ZTwhere it currently is to be found.  The system always knows where the pieces of the °dONLNdıV˚b(_˚system°dONLNd¸bZn¿(kZare, and will always °dONLNdb¿n)fBknow for every incarnation of the Macintosh.  There are low-memory°dONLNdTnZzÅ(wZ=global variables for all of the pieces of hardware currently °dONLNdënÅz(wÅfound in the Macintosh.  This°dONLNdØzZÜ0(ÉZ+includes the VIA, the SCC, the Sound Chip, °dONLNd⁄z0Ü)÷-the IWM, and the video display.  Whenever you°dONLNdÜZí‘(èZLare stuck with going to the hardware, use the low-memory globals.  The fact °dONLNdTÜ‘í(è‘that a program°dONLNdcíZûå(õZ@goes directly to the hardware means that it is risking imminent °dONLNd£íåû(õåincompatibility, but using the°dONLNd¬ûZ™º(ßZlow-memory global °dONLNd‘ûº™)bGwill ensure that the program has the best odds.  It’s like going to Las°dONLNd™Z∂Î(≥ZVegas:  if you don’t gamble at °dONLNd;™Î∂)ë>all, you don’t lose any money; if you have to gamble, play the°dONLNdz∂Z¬Ó(øZ game that you lose the least on.°dONLNdõŒZ⁄Á*How to find hard attacks°dONLNd¥ÊZÚs*8Run the suspicious program on the Macintosh XL.  Nearly °dONLNdÏÊsÚ(Ôs%all of the hardware is in a different°dONLNdÚZ˛¶(˚ZCmemory location on the XL.  If a program has a hard-coded hardware °dONLNdUÚ¶˛(˚¶address in it, it will fail.°dONLNds˛Z
  774. {(ZIt may °dONLNdz˛{
  775. )!Wcrash, or it might not perform the desired task, but it won’t work as advertised.  This°dONLNd“
  776. Zfl(ZOunfortunately, is not a completely legitimate test, since the XL does not have °dONLNd    !
  777. fl(fl some of the°dONLNd    -Z"Û(ZWhardware of other Macintoshes, and some of the hardware that is there has the register °dONLNd    ÑÛ"(Ûmapping°dONLNd    å"Z.Í(+ZWdifferent.  This means that it is possible to play by the rule of using the low-memory °dONLNd    „"Í.(+Í
  778. global and°dONLNd    Ó.Z:º(7Zstill be incompatible.°dONLNd
  779. FZR * Don’t use bits that are reserved°dONLNd
  780. (^ZjÊ*KOccasionally during the life of a Macintosh programmer, there comes a time °dONLNd
  781. s^Êj(gÊ
  782. when it is°dONLNd
  783. ~jZv(sZZnecessary to bite the bullet and use a low-memory global.  These are very sad days, since °dONLNd
  784. ÿjv(sit has°dONLNd
  785. flvZÇë(Z@been demonstrated (by history) that low-memory global variables °dONLNd vëÇ(ëare a mysterious lot, and not°dONLNd =ÉZè3(åZ/altogether friendly.  One fellow in particular °dONLNd lÉ3èu)Ÿ is known as °dONLNd xÇuéò)BROM85°dONLNd }Éòè¸)#, a word located at °dONLNd ëǸé)d$28E°dONLNd ïÉè).°dONLNd òèZõ…(òZThis particular variable °dONLNd ±è…õ)oChas been documented as the way to determine if a program is running°dONLNd ıõZß°(§ZDon the 128K ROMs or not.  Notably, the top most bit of that word is °dONLNd 9õ°ß(§°the determining bit.  This°dONLNd TßZ≥è(∞Z means that °dONLNd _ßè≥)5Tthe rest of the bits in that word are reserved, since nothing is described about any ¿X¿
  786. (’ZOV 4 - Compatibility: Why & How(’˙19)
  787.  of 21(ÎZOverviewˇ⁄HRˇ ˇˇˇˇRH
  788. HR,Times
  789. .+6-Macintosh Technical Notes /4/˘
  790. °dONLNd<6H6*8further bits.  Remember, if it doesn’t say, assume it’s °dONLNd8<6H¯(E6)reserved.  If it’s reserved, don’t depend°dONLNdbH6T^(Q6    upon it. °dONLNdkH^T¯)(T Take the cautious way out and assume that the other bits that aren’t documented are°dONLNd¿T6`=(]68used for Switcher local variables, or something equally °dONLNd¯T=`¯(]=$wild.  An example of a bad way to do°dONLNd`6lå(i6the comparison is:,
  791. Courier
  792.     °dONLNd1xZÉÕ+$VAR  Rom85Ptr: WordPtr;°dONLNdJÇZç“*
  793.      RomsAre64: Boolean;°dONLNddòZ£ü*ARom85Ptr := Pointer ($28E);    { point at the low-memory global }°dONLNdߢZ≠ê*
  794. >IF  Rom85Ptr^ = $7FFF  THEN  RomsAre64 := False  { Bad test. }°dONLNdÁ¨Z∑“*
  795. ELSE  RomsAre64 := True;
  796. °dONLNd¬6Œu(À6This is a bad °dONLNd¬uŒ^)?2test since the comparison is testing the value of °dONLNd@¬^Œl)Èall°dONLNdC¬lŒ¯) of the bits, not only the one°dONLNdbŒ6⁄á(◊6Hthat is valid.  Since the other bits are undocumented, it is impossible °dONLNd™Œá⁄¯(◊áto know what they are°dONLNd¿⁄6ÊD(„67used for.   Assume they are used for something that is °dONLNd˜⁄Dʯ(„D%arbitrarily random, and take the safe°dONLNdÊ6Ú`(Ô6way out.°dONLNd&˛6
  797.  *How to avoid being bitten
  798.     °dONLNdAZ!√+$VAR     ROM85Ptr: Ptr°dONLNdX,Z7ü*ARom85Ptr := Pointer ($28E);    { point at the low-memory global }°dONLNdõ6ZA∏*
  799. FIF BitTst(ROM85Ptr,0) THEN RomsAre64 := True {Good--tests only hi-bit}°dONLNd„@ZK◊*
  800. ELSE  RomsAre64 := False;
  801. °dONLNd˝V6b(_60This technique will ensure that when those bits °dONLNd-Vb¯)‡+are documented, your program won’t be using°dONLNdYb6n"(k62them for the wrong things.  Beware of trojan bits.°dONLNdéz6Ü@*4Don’t use undocumented stuff.  Be very careful when °dONLNd¬z@ܯ(É@$you use anything out of the ordinary°dONLNdÁá6ì”(ê6"stream of a high-level language.  °dONLNd    á”ì5)ùFor instance, in the °dONLNdÜ5íX)bROM85°dONLNd#áXì¯)#" case, it is very easy to make the°dONLNdFì6üß(ú6Lmistake of checking for an absolute value instead of testing the actual bit °dONLNdíìßü¯(úßthat encodes the°dONLNd£ü6´â(®6>information.  Whenever a program is using low-memory globals, °dONLNd·üâ´¯(®âbe sure that only the°dONLNd˜´6∑|(¥6Binformation desired is being used, and not some undocumented (and °dONLNd9´|∑¯(¥|hence reserved) bits.  It’s°dONLNdU∑6√(¿6.not always easy to determine what is reserved °dONLNdÉ∑√¯)„+and what isn’t, so conservative programmers°dONLNdØ√6œb(Ã6@always use as little as possible.  Be wary of the strange bits, °dONLNdÔ√bœ¯(Ãband accept rides from none of°dONLNd
  802. œ6€l(ÿ6@them.  The ride you take might cause you to revise your program.°dONLNdNÁ6Û¡*How to find those bitten°dONLNdgˇ6 ü*Since there are such a °dONLNd~ˇü ¯)iImultitude of possible places to get killed, there is no simple way to see°dONLNd» 6((64what programs are using illegal bits.  As time goes °dONLNd¸ (¯)Ú,by it will be possible to find more of these°dONLNd)6#å( 6cases by running °dONLNd:å#¯)VHon various versions of the Macintosh, but there will probably never be a°dONLNdÉ#6/˛(,6(comprehensive way of finding out who is °dONLNd´#˛/¯)»2accepting strange rides, and who is not.  Whenever°dONLNdfi/6;é(86Mthe use of a bit changes from reserved status to active, it will be possible °dONLNd    +/é;¯(8éto find those bugs via°dONLNd    B;6G%(D62extensive testing.  From a source level, it would °dONLNd    t;%GÙ)Ô(be advisable to look over any use of low°dONLNd    ú;ÙG¯)œ-°dONLNd    ùH6Tfl(Q6Zmemory globals, and eye them closely for inappropriate bit usage.  Do a global search for °dONLNd    ˜HflTÒ(Qflthe °dONLNd    ˚GÒS¯)$°dONLNd    ˝T6`»(]6R(which describes those ubiquitous hexadecimal numbers), and when found see if the °dONLNd
  803. OT»`¯(]»
  804. use of the°dONLNd
  805. Z`6ld(i6Anumber is appropriate.  Trust no one that is not known.  If they °dONLNd
  806. õ`dl¯(idare documented, they will stay°dONLNd
  807. ∫l6xX(u6<where they are, and have the same meaning.  Be very careful °dONLNd
  808. ˆlXx¯(uX in realms that are undocumented.°dONLNd x6ÑÁ(Å6ZBits that suddenly jump from reserved to active status have been known to cause more than °dONLNd rxÁѯ(ÅÁone°dONLNd vÑ6ê5(ç65program to have a sudden anxiety attack.  It is very °dONLNd ´Ñ5ê¯)ˇ$unnerving to watch a program go from°dONLNd –ê6ú…(ô6Tcalm and reassuring to rabid status.  Users have been known to drop their keyboards °dONLNd $ê…ú¯(ô…    in sudden°dONLNd .ú6®Ò(•6&shock (which is bad on the keyboards). ¿4¿˘
  809. *020)
  810.  of 21(’fOV 4 - Compatibility: Why & How+lOverviewˇBHRˇ ˇˇˇˇRH
  811. HR,Times
  812. .+Z-Developer Support Center(-Ï
  813. March 1988 /X/
  814. °dONLNdHZWò(TZSummary
  815. °dONLNdcZo*\So what does all this mean?  It means that it is getting harder and harder to get away with °dONLNddco(lminor°dONLNdjoZ{…(xZLbugs in programs.  The minor bugs of yesterday are the major ones of today. °dONLNd∂o…{(x… No one will yell°dONLNd»{Záé(ÑZ@at you for having bugs in your program, since all programs have °dONLNd{éá(Ñébugs of one form or another.°dONLNd&áZì(êZVThe goal should be to make the programs run as smoothly and effortlessly as possible. °dONLNd|áì(ê The°dONLNdÅìZüW(úZ4end-users will never object to bug-reduced programs.°dONLNd∂´Z∑ø*What is the best way °dONLNdÀ´ø∑)eIto test a program?  A reasonably comprehensive test is to exercise all of°dONLNd∑Z√^(¿Z7the program’s functions under the following situations:°dONLNdMœl€p+•°dONLNdOœy€Î)
  816. NUse Discipline to be sure the program does not pass illegal things to the ROM.°dONLNdû€lÁp(‰l•°dONLNd†€yÁ¡)
  817. =Use heap scramble and heap purge to be sure that handles are °dONLNd›€¡Á¯(‰¡
  818. being used°dONLNdËÁyÛ¬(yDcorrectly, and that the memory management of the program is correct.°dONLNd-Ûlˇp(¸l•°dONLNd/Ûyˇ)
  819. Run with a checksum on memory °dONLNdMÛˇ¯)¢/locations 0...3 to see if the program writes to°dONLNd}ˇy ¬(ythese locations.°dONLNdé lp(l•°dONLNdê yU)
  820. +Run on a 128K Macintosh, or under Switcher °dONLNdª U¯)‹"with a small partition, to see how°dONLNdfiy#f( y2the program deals with memory-critical situations.°dONLNd#l/p(,l•°dONLNd#y/ü)
  821. Run on °dONLNd#ü/¯)&Da 68020 system to see if the program is 68020-compatible and to make°dONLNd_/y;ò(8y:sure that changing system speed won’t confuse the program.°dONLNdö;lGp(Dl•°dONLNdú;yGß)
  822.     Run on a °dONLNd•;ßG¯).AMacintosh XL to be sure that the program does not assume too much°dONLNdÁGyS(Py8about the operating system, and to test screen handling.°dONLNd Sl_p(\l•°dONLNd"Sy_Á)
  823. Run on an Ultra-Large °dONLNd8SÁ_¯)n:screen to be sure that the screen handling is correct, and°dONLNds_ykZ(hy/that there are no hard-coded screen dimensions.°dONLNd£klwp(tl•°dONLNd•kyw)
  824. Run on 64K ROM machines to °dONLNd¿kw¯)ñ.be sure new traps are not being used when they°dONLNdÔwyÉØ(Äy don’t exist.°dONLNd¸Élèp(ål•°dONLNd˛ÉyèI)
  825. &Run under both HFS and MFS to be sure °dONLNd$ÉIè¯)–$that the program deals with the file°dONLNdIèyõq(òy3system correctly.  (400K floppies are usually MFS.)°dONLNd}ßZ≥Ø(∞ZIf a program can °dONLNdéߨ≥)UIlive through all of this with no Discipline traps, no checksum breaks, no°dONLNdÿ≥Zø(ºZ[system errors, no anomalies, no data loss and still get useful work done, then you deserve °dONLNd3≥ø(ºa°dONLNd5øZÀr(»Zgold °dONLNd:ørÀ)Smedal for programming excellence.   Maybe even an extra medal for conduct above and°dONLNdéÀZ◊…(‘ZMbeyond the call of duty.  In any case, you will know that you have done your °dONLNd€À…◊(‘…job about as well°dONLNdÌ◊Z„Á(‡ZTas it can be done, with today’s version of the rules, and today’s programming tools.°dONLNdBÔZ˚ù*Sounds like a °dONLNdPÔù˚)CNforeboding task, doesn’t it?  The engineers in Macintosh Technical Support are°dONLNdü˚Z€(Zavailable to help you with °dONLNd∫˚€)Å?compatibility issues (we won’t always be able to talk about new°dONLNd˙Z(Z&products, since we love our jobs, but °dONLNd     )∫3we can give you some hints about compatibility with°dONLNd    TZΔ(Zwhat the future holds).°dONLNd    l+Z7ê*
  826. Good luck.°dONLNd    w[Zgƒ*0Further Reference: gXg°dONLNd    ähltp+
  827. •°dONLNd    åh~t\)-Technical Note OV 3 - Compatibility Guidlines°dONLNd    ∫tlÄp(}l•°dONLNd    ºt~Ä=)%Technical Note OV 15 - Debugging Tips ¿X¿
  828. (’ZOV 4 - Compatibility: Why & How(’˙21)
  829.  of 21(ÎZOverviewˇ